home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / clang / ctask.zip / CTASK.DOC < prev    next >
Text File  |  1988-07-01  |  130KB  |  2,800 lines

  1.           CTask Manual          Version 1.1  88-07-01                Page 1
  2.  
  3.  
  4.  
  5.  
  6.                                         CTask
  7.                              A Multitasking Kernel for C
  8.  
  9.                             Version 1.1  Released 88-07-01
  10.  
  11.                           Public Domain Software written by
  12.  
  13.                                     Thomas Wagner
  14.                                   Patschkauer Weg 31
  15.                                    D-1000 Berlin 33
  16.                                      West Germany
  17.  
  18.                                    BIXmail: twagner
  19.  
  20.  
  21.                                      Introduction
  22.                                      ============
  23.  
  24.           CTask  is a set of routines that allow your C program  to  execute 
  25.           functions  in parallel, without you having to build  in  sophisti-
  26.           cated  polling and switching schemes. CTask handles the  switching 
  27.           of processor time with a priority based, preemptive scheduler, and 
  28.           provides a fairly complete set of routines for inter-task communi-
  29.           cation,  event signalling, and task interlocking.  CTask also  in-
  30.           cludes  a  number of drivers for MS-DOS that build  on  the  basic 
  31.           functions  to allow you to include serial I/O, printer  buffering, 
  32.           and  concurrent  access to DOS functions into your  programs  with 
  33.           little programming effort.
  34.  
  35.                                       An Example
  36.  
  37.           To  illustrate one possible use of CTask, let me elaborate on  the 
  38.           following  example. Say you just finished your nifty  telecommuni-
  39.           cations  program, complete with download protocols,  scripts,  and 
  40.           everything.  But wouldn't it be nice to be able to print the  file 
  41.           you  just downloaded while receiving the next, and edit a  comment 
  42.           to some previous message without interrupting the transmission? So 
  43.           you take those editor routines from a previous project, plug  them 
  44.           in, and - oops, how do you switch back and forth between  editing, 
  45.           communication  and  printing? The answer to this is  CTask.  CTask 
  46.           allows your C program to do many different things at the same time 
  47.           by switching the processor between the tasks you define. And since 
  48.           most  of  the time your program is waiting for  some  slow  device 
  49.           (like the human hand) to provide feedback, this switching is  com-
  50.           pletely  transparent,  and will not noticeably slow  your  program 
  51.           down.  
  52.  
  53.                                 Switching the Context
  54.  
  55.           So  what is needed to allow the user to edit a file while  at  the 
  56.           same  time  downloading another and printing a third?  First,  you 
  57.           have to have some form of *context switching*. This means that you 
  58.           have  to be able to interrupt the processing of the download  when 
  59.           CTask Manual          Version 1.1  88-07-01                Page 2
  60.  
  61.  
  62.  
  63.           the  user presses a key, process the key, and return to the  down-
  64.           load  *task*  at  the exact same point  it  was  interrupted.  One 
  65.           solution  to this would be to include a poll for the  keyboard  at 
  66.           several points in the download routine, and call the editor *task* 
  67.           when a key is available.  But apart from cluttering your code with 
  68.           lots  of unrelated calls, there is another problem.  What  if  the 
  69.           operation  the user requested is more involved than  just  putting 
  70.           the character on the screen, like writing the file to disk?   This 
  71.           might  take so long that your download times out. There must be  a 
  72.           way  to  pass control back and forth between the two  tasks,  such 
  73.           that  no task is delayed for an extended period of time, and  also 
  74.           to  activate  the print spooler task at some defined  interval  to 
  75.           output  the data to the printer. This context switching is  called 
  76.           *scheduling* in CTask. The *scheduler* is invoked on every  system 
  77.           timer  tick,  and will save the context of the current  task.  The 
  78.           scheduler  then  takes the first element from the queue  of  tasks 
  79.           that  are  eligible to be run, and restores the  context  of  this 
  80.           task, returning to the point where the task was interrupted.  This 
  81.           switching  is completely automatic, and requires no  special  pro-
  82.           gramming in the tasks itself. All you have to do is to tell  CTask 
  83.           that  there are three tasks, the download task, the spooler  task, 
  84.           and the editor task.
  85.  
  86.                                     You have Mail
  87.  
  88.           All  you have to do for context switching, that is. There's a  bit 
  89.           more  to  multitasking  than meets the eye. How do  you  tell  the 
  90.           spooler task what files to spool, and the download task what files 
  91.           to  download? You can't call a task like an ordinary function,  so 
  92.           what  you need for this is *inter-task communication*. There  must 
  93.           be  a way to pass a message containing the filename to be  printed 
  94.           to  the spooler task, and there are several in CTask, one of  them 
  95.           the  *mailbox*.  The spooler can use a CTask call,  wait_mail,  to 
  96.           wait  for a message to arrive at its mailbox. As long  as  nothing 
  97.           arrives, the spooler task will no longer be scheduled, so if there 
  98.           is nothing to print, it will not use any processor time. When  you 
  99.           send  a  message with send_mail to the mailbox, the  spooler  will 
  100.           wake  up, and process the file. You can also send more  file  name 
  101.           messages to the spooler while it still prints a file, leaving  the 
  102.           messages in the mailbox until the spooler is ready to process  the 
  103.           next file.  
  104.  
  105.                                Reentrancy and Resources
  106.  
  107.           This  last  example  seems  innocent enough,  but  there's  a  big 
  108.           stumbling block hidden in it. You allocate the file name  messages 
  109.           in  the  controlling task with malloc, and you free  them  in  the 
  110.           spooler  with  free,  no problem, right? Wrong,  there  is  a  big 
  111.           problem,  *reentrancy*.  Reentrancy means that you can re-enter  a 
  112.           routine while another task is already using it, and that this will 
  113.           not disturb the operation of the interrupted task. But malloc  and 
  114.           free  share  and  modify global data, the  chain  of  free  memory 
  115.           blocks.  Imagine  the following: You just called malloc  from  the 
  116.           controlling task. Malloc has loaded the address of a free  element 
  117.           CTask Manual          Version 1.1  88-07-01                Page 3
  118.  
  119.  
  120.  
  121.           into  a local variable, and is about to write back the pointer  to 
  122.           the  next free element into the last. At exactly this moment,  the 
  123.           timer  ticks, and the spooler is activated.  It has just  finished 
  124.           printing,  so it calls free. Free steps through the chain of  free 
  125.           blocks  to find the right place to insert the block. According  to 
  126.           Murphy's law, it will find just the place where malloc is about to 
  127.           write back the pointer. Free coerces the elements, points the next 
  128.           pointer  to the element malloc just wants to take off  the  chain, 
  129.           and returns. Malloc writes its next pointer into the middle of the 
  130.           newly coerced block, and now returns an element which is still  in 
  131.           the  free list. Compared to the job of finding this kind  of  bug, 
  132.           stepping  in for Tantalus may feel like a vacation. This  kind  of 
  133.           problem  code is called a *critical region*.  There must be a  way 
  134.           to make sure that no two tasks simultaneously enter such a region, 
  135.           and, you guessed it, CTask provides one, the *resource*. When  you 
  136.           call  request_resource  in  one task, all other  tasks  trying  to 
  137.           request  the same resource after that are put to sleep  until  you 
  138.           call  release_resource. Only then will the highest  priority  task 
  139.           that waits for the resource wake up, and get access to the protec-
  140.           ted region. So you would have to substitute malloc and free  calls 
  141.           in  your  routines with calls to functions that  first  request  a 
  142.           resource, execute the function, and then release the resource.
  143.  
  144.                                       DOS Access
  145.  
  146.           But, you might ask, isn't there another reentrancy problem in this 
  147.           example, since both the spooler and the download task might simul-
  148.           taneously call DOS to do their file-I/O, and DOS is not reentrant? 
  149.           Do I have to substitute all my calls to fread and fwrite, too? The 
  150.           answer  to this, luckily, is no. CTask traps all your  DOS  calls, 
  151.           and automatically encloses them in the necessary resource  request 
  152.           and release calls, so you don't have to worry about trashing  your 
  153.           disk by simultaneous DOS requests. The limited multitasking  capa-
  154.           bilities of DOS are exploited to allow some parallel processing in 
  155.           DOS,  and CTask will also detect and handle DOS calls by  resident 
  156.           background programs like the DOS PRINT utility. 
  157.  
  158.                                  Piping the Keyboard
  159.  
  160.           CTask  also  allows you to circumvent DOS for keyboard  input,  so 
  161.           that  waiting  for the keyboard will not block  other  tasks  from 
  162.           access  to  DOS functions. CTask uses another form  of  inter-task 
  163.           communication  for  storing  keys entered  on  the  keyboard,  the 
  164.           *pipe*.  The pipe is similar to the mailbox in that you  can  wait 
  165.           for  items to be sent to a pipe. But unlike mailboxes,  pipes  use 
  166.           their  own buffer to store the items (which are limited  to  bytes 
  167.           and  words),  so you don't have to allocate mail blocks  for  each 
  168.           item. When waiting on the pipe, your task is put to sleep, so  the 
  169.           processor  is  free  to do more interesting things  than  to  loop 
  170.           waiting for the user to press a key.  When a key is pressed, it is 
  171.           written to the pipe, waking your task. 
  172.  
  173.           CTask Manual          Version 1.1  88-07-01                Page 4
  174.  
  175.  
  176.  
  177.                                Serial I/O and Timeouts
  178.  
  179.           Pipes are also used for the serial I/O handler included with CTask 
  180.           that  makes some of the work you've put into  your  communications 
  181.           package obsolete. When outputting data to the serial port via  the 
  182.           CTask routines, the data is buffered in a pipe, and incoming  data 
  183.           is  also  placed in a pipe. All interrupt handling, and  the  pro-
  184.           cessing of modem status and XON/XOFF protocols, is done by  CTask, 
  185.           so you can concentrate on implementing the higher level protocols. 
  186.           Since CTask allows all calls that wait for pipes, mail, and  other 
  187.           events, to specify a timeout that is based on the system tick, you 
  188.           do  not have to resort to timed waiting loops to  detect  communi-
  189.           cation line faults.  
  190.  
  191.                                       Priorities
  192.  
  193.           If the protocol you implement requires fast responses to  incoming 
  194.           blocks,  you  can  influence the response of CTask  to  your  comm 
  195.           task's  needs by giving this task a higher priority. CTask  allows 
  196.           65535 different priority levels, and tasks having higher  priority 
  197.           are  scheduled  before  tasks  with  lower  priority.  Also,  high 
  198.           priority  tasks  will get access to mail,  pipes,  and  resources, 
  199.           before  other tasks. It might even be sensible to split  the  comm 
  200.           task into two separate tasks, one of high priority that  assembles 
  201.           the  incoming  bytes into blocks and handles the protocol,  and  a 
  202.           lower priority task that reads the received blocks from a  mailbox 
  203.           and stores them on the disk. In extremely time critical applicati-
  204.           ons, you can even turn off task preemption, so the timer tick will 
  205.           no longer cause a task switch.  
  206.  
  207.                                 Change to your liking
  208.  
  209.           CTask provides all basic building blocks for implementing  concur-
  210.           rent  programs in an easy and comprehensible way. Since  CTask  is 
  211.           mainly  implemented  in  C, it may not  be  the  fastest  possible 
  212.           system,  but  due  to the straightforward design  which  uses  few 
  213.           shortcuts, modifying the sources to suit your needs and taste  can 
  214.           be  done  without weeks of studying assembler code  that  squeezes 
  215.           every microsecond from the processor. CTask is public domain code, 
  216.           and  there  are no restrictions on its use. It is  distributed  in 
  217.           source form, so you are free to change all aspects of the package. 
  218.           Multitasking  programs, especially in embedded applications,  tend 
  219.           to  be  very diverse in their needs for  specific  constructs.  So 
  220.           although CTask is ready to run under DOS, and is easily  adaptable 
  221.           for embedded applications, you should see CTask more as a starting 
  222.           point  for your own thoughts, and as a toolbox from which you  can 
  223.           pick the instruments you need, than as a finished and fixed  block 
  224.           of code you simply plug into your application.
  225.  
  226.           CTask Manual          Version 1.1  88-07-01                Page 5
  227.  
  228.  
  229.  
  230.                                  How does CTask work
  231.                                  ===================
  232.  
  233.                                         Queues
  234.  
  235.           CTask  uses priority queues for most of its functions. A  priority 
  236.           queue differs from the usual FIFO (first in, first out) queues  in 
  237.           the insertion of elements into the queue. Rather than just placing 
  238.           new elements after the last queue member, the priority of the task 
  239.           determines  its place in the queue. A task is enqueued  after  all 
  240.           queue members with higher or equal priority, but before any member 
  241.           with  lower  priority. Removal from the queue takes place  at  the 
  242.           first element. These queues are used with all operations in  which 
  243.           a  task is made waiting, be it waiting to get run, or waiting  for 
  244.           an event. The advantage of this is the simplicity of  implementing 
  245.           priorities. Other schemes might rely on arrays of queues, or sear-
  246.           ching  through  lists, to determine the highest  priority  waiting 
  247.           task. With priority queues, the most time critical function, sche-
  248.           duling, is very fast, since it can simply take the first task from 
  249.           the  queue  of eligible functions. The disadvantage  is  the  time 
  250.           required to step through all greater or equal priority members  of 
  251.           a queue to find the right place to insert the task. But since  the 
  252.           number of tasks simultaneously enqueued in the same queue is  nor-
  253.           mally relatively small with most multitasking systems, this disad-
  254.           vantage is more than offset by the simple, and thus easy to imple-
  255.           ment  efficiently, scheme. The operation of removing a  task  from 
  256.           somewhere in the middle of a queue is rare (it will only occur  on 
  257.           task kill, task wake, or timeout), and thus the overhead of  mana-
  258.           ging a doubly linked list is not justified.
  259.  
  260.                                     The Scheduler 
  261.  
  262.           In  a  CTask system, there is at least one  essential  queue:  the 
  263.           eligible queue, which contains all tasks waiting to run. Each time 
  264.           the scheduler is invoked, it will first save all processor  regis-
  265.           ters  on the current stack, save the stack pointer in the  current 
  266.           tasks  control block, and then enqueue the current task  into  the 
  267.           appropriate  queue.   Normally, this will be the  eligible  queue. 
  268.           Then  the first element in the eligible queue is removed from  the 
  269.           queue,  the stack is switched to the stack of this task, and  pro-
  270.           cessor registers are restored from the new stack, returning to the 
  271.           point where the new task was interrupted. If the eligible queue is 
  272.           empty  the scheduler will loop with interrupts enabled. The  queue 
  273.           head pointer of the task control block of the current running task 
  274.           determines  what  will happen to the task when  the  scheduler  is 
  275.           invoked. If it points to an event control block, the task will  be 
  276.           enqueued  as waiting for this event. If it is NULL, the task  will 
  277.           not  be  enqueued  in any queue, effectively stopping  it.  If  it 
  278.           points to the eligible queue, the task will be enqueued there.  In 
  279.           any case, the scheduler does not care what queue the task is to be 
  280.           enqueued  in, since all queues use the same priority based  order-
  281.           ing. If a task is no longer in the eligible queue, it can only  be 
  282.           returned to this queue by an external event. 
  283.  
  284.           CTask Manual          Version 1.1  88-07-01                Page 6
  285.  
  286.  
  287.  
  288.                                         Events 
  289.  
  290.           External  events  can  take several forms,  which  are  relatively 
  291.           similar on the inside. All CTask events use a control block,  with 
  292.           at  least one queue for tasks waiting for the event.  Although  it 
  293.           would  have been possible to summarize all events under  a  global 
  294.           structure,  this approach was not used in CTask, the main  reasons 
  295.           being execution speed and ease of use. So if you scan through  the 
  296.           source  files  for CTask events, you will see  many  very  similar 
  297.           routines, which only differ in the types of data and the names  of 
  298.           queues they process. In a class based language like C++, one would 
  299.           certainly  define  all events as instances or derivations  of  one 
  300.           class.  In plain C, the necessary type casting, and the  different 
  301.           handling of certain cases, would clobber the code to the point  of 
  302.           illegibility.
  303.  
  304.                                       Resources
  305.  
  306.           The  event types "resource", "flag", and "counter" differ only  in 
  307.           the  kind of wait operations and the handling of the  state  vari-
  308.           able.  The  resource is mainly for use  in  interlocking  critical 
  309.           regions  of  code, to protect access to  non-reentrant  resources. 
  310.           Only one task can "own" a resource, all other tasks requesting the 
  311.           resource are delayed until the owning task releases it. For  added 
  312.           protection,  CTask  stores the task control block address  of  the 
  313.           current owner of the resource in the resource control block, so no 
  314.           other task can erroneously release it. 
  315.  
  316.                                         Flags
  317.  
  318.           Flags, on the other hand, can be set and cleared by any task,  and 
  319.           also by interrupt handlers. Tasks can wait on either state of  the 
  320.           flag,  and all tasks waiting for a state are simultaneously  acti-
  321.           vated  when  the flag is changed to this state. This  makes  flags 
  322.           suitable for use as a global signalling mechanism.
  323.  
  324.                                        Counters
  325.  
  326.           Counters  are  a variation on the flag concept. A counter  can  be 
  327.           incremented  and cleared by any task, and by  interrupt  handlers. 
  328.           Tasks  can wait for a counter to be zero or nonzero.  Like  flags, 
  329.           all tasks waiting for the zero state are activated simultaneously. 
  330.           But  unlike  flags, only the first task waiting  for  the  nonzero 
  331.           state of a counter will be activated on incrementing the  counter, 
  332.           and  the  counter will be automatically decremented  by  one.  The 
  333.           counter is used inside CTask to handle timer interrupts. The timer 
  334.           counter  will  be incremented on each timer tick,  activating  the 
  335.           timer task. If for any reason the timer task is unable to complete 
  336.           its run until the next tick, this tick will not be lost, since the 
  337.           counter  is incremented, and the timer task will continue  to  run 
  338.           the next time it calls the counter wait request. 
  339.  
  340.           CTask Manual          Version 1.1  88-07-01                Page 7
  341.  
  342.  
  343.  
  344.                                  Mailboxes and Pipes
  345.  
  346.           The  "mailbox"  and  "pipe"  events can  be  used  for  inter-task 
  347.           communication,   and  for  the  communication  between   interrupt 
  348.           handlers and tasks.
  349.  
  350.                                       Mailboxes
  351.  
  352.           Mailboxes can hold an unlimited number of mail blocks. Mail blocks 
  353.           have  no fixed structure, but the first doubleword in  each  block 
  354.           passed  to  a  mailbox routine is used as a  chain  pointer.  Mail 
  355.           blocks  are chained into a mailbox in FIFO order. Tasks  can  wait 
  356.           for  mail to arrive, or can conditionally read mail if a block  is 
  357.           available.  Tasks  and interrupt handlers can write  blocks  to  a 
  358.           mailbox. Since mailboxes don't need any copying of data, they  are 
  359.           suited  for high speed exchange of larger amounts of data  between 
  360.           tasks. The disadvantage of sending mail this way is that you  have 
  361.           to  make sure that a mail block is not re-used before it has  been 
  362.           read  out  of the box and processed by the  receiving  task.  When 
  363.           exchanging fixed length messages, you can build a free mail  block 
  364.           chain by using a mailbox to hold the available blocks. 
  365.  
  366.                                   Pipes and Buffers
  367.  
  368.           Buffered message exchange is possible using pipes. When creating a 
  369.           pipe,  you  specify  a buffer area and its length,  and  the  pipe 
  370.           routines  will buffer all data written to the pipe in  this  area. 
  371.           This implies that writing to a pipe may cause a task to be delayed 
  372.           until  there  is space available in the pipe. To  allow  interrupt 
  373.           handlers to write to pipes, there is a conditional write  request, 
  374.           which  will simply return if the pipe is full. Tasks can wait  for 
  375.           data  to  arrive  in a pipe, and for the pipe  to  be  emptied.  A 
  376.           conditional  read  request is also provided. The  disadvantage  of 
  377.           pipes  is that they are slightly slower than mailboxes due to  the 
  378.           necessary copying, and that you can only place word or byte  sized 
  379.           items  in  a pipe. When there is more than one  reader  or  writer 
  380.           task,  you  can  not  rely on the bytes in a  pipe  being  in  any 
  381.           specific  order.  A "buffer" construct is provided in  CTask  that 
  382.           expands pipes to allow arbitrary length messages to be written and 
  383.           read. This is implemented using a resource for reading and writing 
  384.           to  the pipe associated with the buffer, so the message is  always 
  385.           guaranteed  to  be  written  and read  in  one  piece.  But  since 
  386.           resources  can  not  be used in  interrupt  handlers,  using  such 
  387.           buffers is not allowed from interrupts.
  388.  
  389.                                      Applications
  390.  
  391.           There  are a number of routines included in the CTask package  for 
  392.           PC-specific tasks. Although they will be of limited use for embed-
  393.           ded  applications, studying the serial I/O and  printer  interface 
  394.           routines  will give you some hints on how to use the CTask  kernel 
  395.           for  implementing  your own device drivers. For  PC  based  appli-
  396.           cations,  the routines should be usable with no or little  changes 
  397.           for implementing complete communications packages.
  398.           CTask Manual          Version 1.1  88-07-01                Page 8
  399.  
  400.  
  401.  
  402.  
  403.           The  serial I/O handler uses interrupts for both input and  output 
  404.           of data, and supports both XON/XOFF and RTS/CTS handshake methods. 
  405.           The modem inputs can selectively be enabled to control data trans-
  406.           mission.   Pipes are used for transmit and receive data, with  the 
  407.           buffer  and  its size specified on initialization. Both  COM1  and 
  408.           COM2 can be active simultaneously, and other ports can be  suppor-
  409.           ted by editing a table in the source code. Since CTask allows  the 
  410.           access  to  all events in interrupt  handlers,  writing  interrupt 
  411.           based I/O drivers is relatively simple.  On receiving a character, 
  412.           the character and any associated errors are placed in the  receive 
  413.           pipe,  and the transmit pipe is read on transmit ready  interrupt. 
  414.           Note  that  conditional reads and writes have to be  used  in  the 
  415.           interrupt  handler,  since  you can't safely  delay  an  interrupt 
  416.           handler.
  417.  
  418.           The  printer  output driver supports both  polling  and  interrupt 
  419.           output.  Again, a pipe is used for the output characters. However, 
  420.           since polling is supported, and because of the somewhat unreliable 
  421.           interrupt  structure  of the printer ports, a driver task  is  re-
  422.           quired.   This  task is automatically created  on  installing  the 
  423.           driver.  The  task first waits on the pipe for a character  to  be 
  424.           written  to  the printer.  When polling is enabled,  it  tries  to 
  425.           output the character directly, using a short busy-waiting loop. So 
  426.           as  not  to load the system too heavily by the polling,  the  task 
  427.           will delay itself for a defined amount of timer ticks if it  can't 
  428.           output  the character after a small number of loops.  When  inter-
  429.           rupts  are  enabled, the process is essentially the  same  at  the 
  430.           start,  but after the character is written to the port,  the  task 
  431.           will wait on a flag to be set. The interrupt handler will set  the 
  432.           flag  on  interrupt, enabling the task to get the  next  character 
  433.           from the pipe. Since interrupts are unreliable with most  printers 
  434.           due to the usually very short pulses on the acknowledge line,  the 
  435.           task  uses a timeout on the flag wait, so it does not hang  if  an 
  436.           interrupt  is missed. Studying the printer driver will  also  give 
  437.           you  an idea what the optional parameter on task creation  can  be 
  438.           used  for. In the printer driver, this parameter is used  to  pass 
  439.           the address of the printer control block, which contains the pipe, 
  440.           the  flag,  and the hardware info, to the task.  This  allows  the 
  441.           printer  driver to be simultaneously installed for any  number  of 
  442.           printers without having to write separate printer tasks. 
  443.           CTask Manual          Version 1.1  88-07-01                Page 9
  444.  
  445.  
  446.  
  447.                                     General Notes
  448.                                     =============
  449.  
  450.                            What can CTask NOT be used for?
  451.  
  452.           CTask  is not intended to provide for multitasking on the  command 
  453.           level of MS-DOS.  Although CTask includes a module for  channeling 
  454.           simultaneous  DOS requests inside a program,  the strategy used in 
  455.           this module is not sufficient for the functionality required  when 
  456.           switching between programs. Adding this functionality would not be 
  457.           trivial (although certainly worthwhile).
  458.           Also, there is no warranty that CTask does perform without errors, 
  459.           or  does exactly what you or I intended.  So CTask should  not  be 
  460.           used  in  applications  in which malfunction of routines  of  this 
  461.           package would result in damage to property or health of any person 
  462.           without  *very*  extensive testing under all kinds  of  loads.  In 
  463.           using CTask, you do so at your own risk.
  464.  
  465.  
  466.                             What is required to use CTask?
  467.  
  468.           To  compile  CTask,  Microsoft C 5.0 or later,  or Turbo C 1.0  or 
  469.           later  are required.  Microsoft MASM 5.0 or later is required  for 
  470.           the assembler parts.  Conversion to other compilers is possible if 
  471.           they  conform to the new ANSI (draft) standard. Conversion of  the 
  472.           assembler parts to other Assembler versions requires  substitution 
  473.           of  the  simplified  model directives by  explicit  segment  defi-
  474.           nitions, and adding the DGROUP to offsets referencing data.
  475.  
  476.           CTask will add 8-12k of code to your program, depending on options 
  477.           and  installed drivers. The minimum static data used by  CTask  is 
  478.           approximately 4k.
  479.  
  480.           Converting  CTask for stand-alone operation requires few  changes. 
  481.           Mainly,  the  timer interrupt handler (in "tsktim.asm") has to  be 
  482.           rewritten,  and the initialization code (in "tskmain.c") may  have 
  483.           to  be  changed. Changes to other modules  (naturally  except  the 
  484.           optional hardware drivers) should not be necessary.
  485.  
  486.           Another requirement is a good debugger.  If you never before wrote 
  487.           multitasking  applications,  you're  in for  some  surprises.  The 
  488.           normal debugging tools (Symdeb, Codeview) are of only limited use, 
  489.           since they use DOS calls for their I/O, and thus may conflict with 
  490.           your background tasks.  One safety measure is to first  thoroughly 
  491.           test  your  program with preemption disabled,  possibly  inserting 
  492.           some schedule() calls, and only allow task preemption if you found 
  493.           most major bugs.  I personally recommend Periscope for  debugging, 
  494.           since  it  can be made resident and so is  always  available,  and 
  495.           because it does not use DOS.  Periscope III is the most  expensive 
  496.           solution,  and the best tool you can imagine (except for Periscope 
  497.           IV,  announced for April),  but the less costly versions will also 
  498.           help a lot.
  499.  
  500.  
  501.           CTask Manual          Version 1.1  88-07-01                Page 10
  502.  
  503.  
  504.  
  505.                           Do I have to pay for using CTask?
  506.  
  507.           No. One reason for writing CTask was to provide a free, no strings 
  508.           attached,  utility, instead of the usual "for personal  use  only" 
  509.           restriction.  Writing a multitasking application for personal  use 
  510.           only doesn't seem too interesting to me. CTask is completely free, 
  511.           and there is no restriction on its use. You may incorporate all or 
  512.           parts of CTask in your programs, and redistribute it in source  or 
  513.           binary form by any means. I also do not restrict the use of  CTask 
  514.           in  commercial  applications.  Since  trying  to  distribute   the 
  515.           unmodified CTask for money will only give you a bad name, you  may 
  516.           even do that if you find someone dumb enough to buy it. Naturally, 
  517.           if  you make a bundle from it, or simply like CTask, I  would  not 
  518.           reject a donation. However, this is not required, and it will  not 
  519.           give you any additional support.
  520.  
  521.  
  522.                               What support can I expect?
  523.  
  524.           I  will try my best to eliminate any bugs reported to me,  and  to 
  525.           incorporate suggested enhancements and changes.  However, my spare 
  526.           time  is limited,  so I can not guarantee continued or  individual 
  527.           support.  (But since I am a free-lance consultant,  you can always 
  528.           hire me to do it...) 
  529.  
  530.           At the time of this writing,  I'm connecting to BIX almost  daily. 
  531.           Problems  of limited general interest can be reported via BIXmail, 
  532.           suggested enhancements and changes,  plus bug reports,  should  be 
  533.           posted  in the c.language/tools conference on BIX (until responses 
  534.           warrant a new conference,  or I can't afford BIX any  longer),  so 
  535.           they can be discussed among all interested (I hope there will be a 
  536.           few).  Normal  mail  can  be used,  too,  but  don't  expect  fast 
  537.           responses.
  538.  
  539.                                   About this Release
  540.  
  541.           Since  the  Beta release of CTask in March, CTask has  been  down-
  542.           loaded  from BIX more than 90 times. Of all downloaders, just  six 
  543.           commented  on  CTasks  features, or sent bug  reports.  The  ideas 
  544.           presented by the commentors are mainly integrated in this version, 
  545.           as  are the fixes for the reported bugs. But since  comments  were 
  546.           sparse,  this is not a revolutionary new release. Many  areas  are 
  547.           completely unchanged, or just slightly modified to accommodate new 
  548.           flags  and  options. See the accompanying file  "changes.doc"  for 
  549.           a summary of the changes.
  550.  
  551.           Although I currently have no new ideas that would warrant  another 
  552.           release,  I  again  would like to invite you to  voice  your  com-
  553.           plaints, suggest enhancements, supply your own code for  inclusion 
  554.           in  the package (note that any code submitted for  inclusion  must 
  555.           not be copyrighted or usage restricted, but I'll naturally respect 
  556.           copyrights  in  code  submitted for  demo  purposes),  or  suggest 
  557.           additions to this manual.
  558.           CTask Manual          Version 1.1  88-07-01                Page 11
  559.  
  560.  
  561.  
  562.                                  Multitasking Basics
  563.                                  ===================
  564.  
  565.                                         Tasks
  566.  
  567.           In CTask, a "task" is defined as a (far) C function. The number of 
  568.           tasks  is  not limited,  and one function may be used for  several 
  569.           tasks.  There  is little difference between a task function and  a 
  570.           normal function. The usual form of a task function is
  571.  
  572.                void far my_task (farptr arg)
  573.                {
  574.                     one-time initialization code
  575.                     while (TRUE)
  576.                       {
  577.                       processing code
  578.                       }
  579.                }
  580.  
  581.           A task function is (usually) never called directly.  Rather, it is 
  582.           specified  in the call to the create_task routine,  and started by 
  583.           start_task.  It will then continue to run,  sharing the  processor 
  584.           time  with  all other tasks,  until it is "killed"  by  kill_task. 
  585.           Returning  from  the routine will have the same effect as a  kill. 
  586.           The sharing of processor time is accomplished by "preempting"  the 
  587.           tasks. Preemption means that the task is interrupted in the middle 
  588.           of some statement by a hardware interrupt (usually the timer), and 
  589.           is *not* immediately restarted when the interrupt handler returns. 
  590.           Instead,  the  next  task that is able to run is activated by  the 
  591.           "scheduler", with the interrupted task continuing its duty at some 
  592.           (normally  unpredictable)  later time.  You can also  have  multi-
  593.           tasking without preemption, and CTask supports this, too, but this 
  594.           requires full cooperation of all tasks in the system, such that no 
  595.           task  continues  to  run for an extended period  of  time  without 
  596.           passing control to other tasks by an explicit scheduling  request, 
  597.           or by waiting for an event.
  598.  
  599.           The  optional  argument  to the task function may be used  if  one 
  600.           function is to be used for more than one task, for example to pass 
  601.           a  pointer to a static data area for use by this specific instance 
  602.           of the function.
  603.  
  604.  
  605.           CTask Manual          Version 1.1  88-07-01                Page 12
  606.  
  607.  
  608.  
  609.                                         Events
  610.  
  611.           Tasks alone would be of limited use.  If you have several routines 
  612.           which  just do number crunching or sorting or  such,  making  them 
  613.           into  parallel  tasks would be sensible only on  a  multiprocessor 
  614.           system.  Normally,  at least some of your tasks will wait for some 
  615.           outside  "event" to happen,  be it the user pressing a key,  or  a 
  616.           character  arriving from the modem.  Then there may be tasks which 
  617.           have to wait until another task finishes processing on some  piece 
  618.           of data before they can continue.  For this synchronization, there 
  619.           are  a number of constructs in CTask,  which I summarize under the 
  620.           name "event".  Common to all events are the operations of  waiting 
  621.           for  an  event  to  happen,  and signalling  that  the  event  has 
  622.           happened.  Using a CTask event is much more efficient than looping 
  623.           while  waiting on some shared data location to change  state,  and 
  624.           also  eliminates  concurrency problems inherent in such  a  simple 
  625.           approach.  Tasks  waiting for an event are taken off the scheduler 
  626.           queue, so they no longer use processor time.
  627.  
  628.                                       Reentrancy
  629.  
  630.           One of the biggest problem with multitasking in general,  and C on 
  631.           the PC in particular, is reentrancy. Reentrancy means that you can 
  632.           use  a routine,  be it you own,  or one of the C run-time library, 
  633.           from different tasks at the same time. When writing your own code, 
  634.           you can easily avoid problems, but when using the run-time library 
  635.           routines,  you often can only guess if the routines are  reentrant 
  636.           or not. 
  637.  
  638.           A routine is NOT reentrant if it modifies static data. This can be 
  639.           illustrated by the following nonsense example:
  640.  
  641.                int non_reentrant (int val)
  642.                {    static int temp;
  643.                     temp = val;
  644.                     return temp * 2;
  645.                }
  646.  
  647.           Now take two tasks,  which call this routine.  Task1 calls it with 
  648.           val=3,  Task2  with val=7.  What will be the return value for both 
  649.           tasks? You never know. There are three possible outcomes:
  650.  
  651.             1) The tasks execute sequentially.  Task1 will get 6,  and Task2 
  652.                14 as a result. This is what one normally expects.
  653.  
  654.             2) Task1  runs  up to "temp = val",  then is interrupted by  the 
  655.                timer.  Task2  executes,  and gets 14 as result.  Then  Task1 
  656.                continues. Return for Task1 is 14.
  657.  
  658.             3) Task2  runs up to "temp = val",  then is interrupted  by  the 
  659.                timer.  Task1  executes,  and  gets 6 as result.  Then  Task2 
  660.                continues. Return for Task2 is 6.
  661.  
  662.           CTask Manual          Version 1.1  88-07-01                Page 13
  663.  
  664.  
  665.  
  666.           add  to  this the effects of optimization,  and a  loop,  and  the 
  667.           outcome is completely random. 
  668.  
  669.           Most  routines in the C library will not explicitly  do  something 
  670.           like this, but all functions that 
  671.  
  672.                - do file I/O (read, write, printf, scanf, etc.) or
  673.                - change memory allocation (malloc, free, etc.)
  674.  
  675.           have  to  use some static data to do buffering,  or to  store  the 
  676.           chain of memory blocks in. Interrupting such an operation may have 
  677.           disastrous effects.  The most devilish aspect of non-reentrancy is 
  678.           that  the  effects are unpredictable.  Your program may  run  1000 
  679.           times  without  any error,  and then on the 1001th time crash  the 
  680.           system so completely that only the big red switch will help.
  681.  
  682.           So  what can you do about it?  A lot.  There are several  ways  to 
  683.           protect  "critical  regions" from being entered in  parallel.  The 
  684.           most simple method is to disable interrupts. This, however, should 
  685.           be  used only for *very* short periods of time,  and not for  rou-
  686.           tines  that  might themselves re-enable them  (like  file-I/O).  A 
  687.           better  method  is to temporarily  disable  task  preemption.  The 
  688.           routines  tsk_dis_preempt  and tsk_ena_preempt allow this form  of 
  689.           short-term task switch disable. Interrupts may still be processed, 
  690.           but  tasks  will  not  be preempted.  The best way  is  to  use  a 
  691.           "resource".  A resource is a special kind of event, which only one 
  692.           task  can  possess  at  a time.  Requesting  the  resource  before 
  693.           entering the routine,  and releasing it afterwards,  will  protect 
  694.           you  from  any  other task simultaneously  entering  the  critical 
  695.           region (assuming that this task also requests the resource).
  696.  
  697.           It  is also reasonably safe to use file-I/O without protection  if 
  698.           it  goes  to  different  files.  The C  file  control  blocks  for 
  699.           different files are distinct, so there will be no conflict between 
  700.           tasks.  Since  DOS  access is automatically  protected  by  CTask, 
  701.           concurrent file I/O is possible.
  702.  
  703.           What  you  may NEVER do is to use a non-reentrant routine that  is 
  704.           not  protected by an interrupt disable from an interrupt  handler. 
  705.           An interrupt handler is not a task,  and so can not safely request 
  706.           a resource or disable task preemption.  This is the reason why the 
  707.           CTask  routines  generally disable interrupts before  manipulating 
  708.           the internal queues rather than only disabling task preemption.
  709.  
  710.  
  711.                                       Deadlocks
  712.  
  713.           One  thing to watch out for when using resources or similar  event 
  714.           mechanisms is not to get into a situation where Task 1 waits for a 
  715.           resource that Task 2 has requested,  while at the same time Task 2 
  716.           waits  for a resource that Task 1 already has.  This situation  is 
  717.           called a deadlock (or,  more picturesque,  deadly embrace), and it 
  718.           can only be resolved with outside help (e.g.  waking up one of the 
  719.           tasks forcibly). To illustrate, consider the following example:
  720.           CTask Manual          Version 1.1  88-07-01                Page 14
  721.  
  722.  
  723.  
  724.  
  725.                void far task_1 ()
  726.                {
  727.                  ...
  728.                  request_resource (&rsc1, 0L);
  729.                  request_resource (&rsc2, 0L);
  730.                  ...
  731.                }
  732.  
  733.                void far task_2 ()
  734.                {
  735.                  ...
  736.                  request_resource (&rsc2, 0L);
  737.                  request_resource (&rsc1, 0L);
  738.                  ...
  739.                }
  740.  
  741.           Since interrupts are always enabled on return from a task  switch, 
  742.           even if the statements are enclosed in a critical region, there is 
  743.           no  guarantee  that  the request_resource calls will  be  executed 
  744.           without interruption. In this example, the problem is obvious, but 
  745.           in  a more complex application,  where resource requests or  other 
  746.           waits might be buried in some nested routine, you should watch out 
  747.           for similar situations. One way to avoid problems would be in this 
  748.           example to change task_2 to
  749.  
  750.                void far task_2 ()
  751.                {
  752.                     int again;
  753.                     ...
  754.                     do {
  755.                          request_resource (&rsc2, 0L);
  756.                          if (again = c_request_resource (&rsc1))
  757.                             {
  758.                             release_resource (&rsc2);
  759.                             delay (2L);
  760.                             }
  761.                        } while (again);
  762.                     ...
  763.                } 
  764.                        
  765.           Note that this is only one of many possible approaches,  and  that 
  766.           this approach favors task_1 over task_2.
  767.  
  768.           You  should also take care not to kill tasks that currently own  a 
  769.           resource. CTask will not detect this, and the resource will  never 
  770.           be freed.
  771.           CTask Manual          Version 1.1  88-07-01                Page 15
  772.  
  773.  
  774.  
  775.                                  Multitasking and DOS
  776.  
  777.           CTask includes (and automatically installs) a routine which  traps 
  778.           all  DOS  calls,  and makes sure that no two tasks  simultaneously 
  779.           enter DOS. This is accomplished using the resource mechanism, with 
  780.           special  provisions  for  the  limited  multitasking  capabilities 
  781.           provided by DOS. There are a few calls, namely those with function 
  782.           codes  <=  0x0c,  which allow functions with codes >  0x0c  to  be 
  783.           executed  while  DOS is waiting for an external device  (generally 
  784.           the keyboard) to get ready.  This, however, limits the use of some 
  785.           C library functions,  namely scanf and fread,  for console  input. 
  786.           Both  these  functions  use  handle input,  and thus  can  not  be 
  787.           interrupted.  When  writing  routines  for  handling  user  input, 
  788.           keyboard  read  functions should either use  the  low-level  calls 
  789.           getch  and  gets,  or,  better yet,  the direct entries  into  the 
  790.           keyboard  handler,  t_read_key and t_keyhit,  and then process the 
  791.           string with sscanf if desired.  The keyboard handler (contained in 
  792.           tskkbd.asm) traps all keyboard interrupts, storing entered keys in 
  793.           a  pipe.  If a task reads from the keyboard,  it is  automatically 
  794.           waiting  for  this pipe.  Using getch and gets is  less  desirable 
  795.           since they use polling instead of waiting, and thus degrade system 
  796.           performance.  Also,  the t_read_key and t_keyhit functions do  not 
  797.           use DOS, so DOS functions <= 0C can be executed concurrently.
  798.  
  799.           You  should  NEVER  circumvent DOS by  calling  the  BIOS-disk-I/O 
  800.           function (INT 13) directly.  This entry is not protected by CTask, 
  801.           and  using it in parallel to DOS file-I/O may send your  hard-disk 
  802.           FAT and directory into never-never land. If you really should need 
  803.           this  interrupt,  you should consider adding support for it in the 
  804.           DOS-handler module "tskdos.asm".  Using the direct sector read and 
  805.           write interrupts 25 and 26 is supported, however. Using other BIOS 
  806.           interrupts  directly  should  also  be  avoided,  unless  you  are 
  807.           absolutely  sure they will not be used by other routines via  DOS, 
  808.           or you provide your own critical region handling.  Using interrupt 
  809.           16, the keyboard interrupt, is safe, since it is redirected to the 
  810.           keyboard handler pipe.
  811.  
  812.           The DOS access module has been tested to work with DOS  3.30,  and 
  813.           with the DOS 3.30 PRINT routine running in the background. Special 
  814.           provisions  are built into the DOS module to detect background DOS 
  815.           calls.  Using Sidekick also hasn't lead to any problems,  although 
  816.           you  may trash Sidekick's screen display by writing to the  screen 
  817.           while  Sidekick is active (note that your application  *continues* 
  818.           to  run  while Sidekick or other pop-ups are active).  I  can  not 
  819.           guarantee  complete compatibility with all background  and  pop-up 
  820.           programs  under all versions of DOS.  Specifically,  DOS  versions 
  821.           prior   to  3.1  have  significant  problems  with   multitasking. 
  822.           Upgrading  to a newer DOS version is recommended if you are  still 
  823.           using DOS < 3.2 (DOS 3.1 has some bugs in other areas).
  824.  
  825.           Critical  errors  and  Control C occurring  while  concurrent  DOS 
  826.           access  takes  place may also be fatal.  Using the  ctrlbrk()  and 
  827.           dosexterr() functions of Turbo C, or their equivalents in MS C, to 
  828.           trap critical errors and Control C is highly recommended.
  829.           CTask Manual          Version 1.1  88-07-01                Page 16
  830.  
  831.  
  832.  
  833.  
  834.                                      Using CTask
  835.                                      ===========
  836.  
  837.           CTask  comes  archived with both source and binaries.  The  binary 
  838.           version is compiled in the large model, but since the pre-compiled 
  839.           kernel  routines don't use any functions from the C  library,  you 
  840.           can  use  all functions in small or other model  programs  (except 
  841.           Tiny). The include files provided specify all model  dependencies, 
  842.           so you don't have to use the large model for your application, but 
  843.           always remember to include "tsk.h" for the type and routine proto-
  844.           types.  The  C  source files will work without  changes  for  both 
  845.           Microsoft  and Turbo C. The library files are not  compatible,  so 
  846.           use "ctaskms.lib" for Microsoft, and "ctasktc.lib" for Turbo C. In 
  847.           the distributed configuration (i.e. dynamic allocation of  control 
  848.           blocks enabled), the file TSKALLOC.C must be added to the  library 
  849.           or separately linked after compiling it *in the same model as  the 
  850.           main  program*. This file uses C-library routines, and  thus  must 
  851.           match  the  main  program's  memory  model.  The  same  goes   for 
  852.           TSKSNAP.C, an optional snapshot-dump utility.
  853.  
  854.  
  855.                                 Configuration Options
  856.  
  857.           The  file TSKCONF.H contains a number of #define's that allow  you 
  858.           to configure some CTask features. The entries are
  859.  
  860.           TSK_DYNAMIC
  861.                If  1,  you can let CTask dynamically create task  and  event 
  862.                control  blocks,  task stacks, and pipe buffers,  by  passing 
  863.                NULL as the block address. Since this requires the C  runtime 
  864.                allocation calls, it is not suitable for non-DOS applications 
  865.                (except if you provide your own memory allocation routines).
  866.  
  867.                This option is normally enabled (1).
  868.                Changing  this  option requires  changing  the  corresponding 
  869.                option in the Assembler macro file TSK.MAC.
  870.  
  871.  
  872.           TSK_NAMEPAR
  873.                If  1, all create_xxx calls accept an  additional  parameter, 
  874.                the name of the created control block. This name should  con-
  875.                tain  only  printeable characters, and should not  be  longer 
  876.                than  8 characters plus the zero terminator. This  option  is 
  877.                used together with TSK_NAMED (see below).
  878.  
  879.                This option is normally enabled (1).
  880.                Changing  this  option requires  changing  the  corresponding 
  881.                option in the Assembler macro file TSK.MAC.
  882.  
  883.  
  884.           TSK_NAMED
  885.                If  1, all control blocks (except timer control  blocks)  are 
  886.                named  and linked on creation. This allows the snapshot  dump 
  887.           CTask Manual          Version 1.1  88-07-01                Page 17
  888.  
  889.  
  890.  
  891.                routine  to  display the system state.  TSK_NAMEPAR  must  be 
  892.                defined for this option to work. Since it may be desirable to 
  893.                switch  off  the handling of the names  after  the  debugging 
  894.                phase, without having to change all create_xxx calls to  omit 
  895.                the  name  parameter,  the  name  parameter  is  ignored   if 
  896.                TSK_NAMED is undefined, but TSK_NAMEPAR is defined. 
  897.  
  898.                This option is normally enabled (1).
  899.                Changing  this  option requires  changing  the  corresponding 
  900.                option in the Assembler macro file TSK.MAC.
  901.  
  902.  
  903.           CLOCK_MSEC     
  904.                If  1, all timeouts are specified in milliseconds instead  of 
  905.                timer  ticks. This allows programming of delays and  timeouts 
  906.                independent of the speedup-parameter or the system tick rate. 
  907.                Since  timeout calculations use floating point operations  if 
  908.                this  option is enabled, a floating point library is  needed. 
  909.                This precludes model independence of the CTask kernel.
  910.  
  911.                This option is normally disabled (0).
  912.  
  913.  
  914.           PRI_TIMER
  915.                Specifies the priority of the timer task. Normally the  timer 
  916.                task should have a higher priority than any other task in the 
  917.                system.
  918.  
  919.                The value is normally 0xf000.
  920.  
  921.  
  922.           PRI_STD
  923.                This  value  may  be used in your programs  as  the  standard 
  924.                priority  of  user tasks. Its value is arbitrary. It  is  not 
  925.                used in the kernel except for the definition of PRI_INT9 (see 
  926.                below).
  927.  
  928.                The value is normally 100 (decimal).
  929.  
  930.  
  931.           PRI_INT9
  932.                Determines the priority of the "int9"-task. This task  chains 
  933.                to  the previous interrupt vector for the sytem  timer  tick, 
  934.                and  thus may activate resident TSR's. Since  TSR's  normally 
  935.                use  polling when accessing the keyboard and  other  devices, 
  936.                the  priority  of the int9-task should be equal to  or  lower 
  937.                than normal user-defined tasks to allow your program to  con-
  938.                tinue to run while the TSR is active. You can tune this value 
  939.                so  that some tasks are blocked by the TSR to avoid  trashing 
  940.                the screen.
  941.  
  942.                The value is normally  PRI_STD - 1.
  943.  
  944.  
  945.           CTask Manual          Version 1.1  88-07-01                Page 18
  946.  
  947.  
  948.  
  949.           IBM
  950.           DOS
  951.                Both IBM and DOS are more or less of informative value  only, 
  952.                to  point out those areas in the kernel that  need  attention 
  953.                when converting to non-IBM or non-DOS environments. Disabling 
  954.                one or both of this options requires the substitution of your 
  955.                own routines for installation and keyboard handling.
  956.  
  957.                Both options must normally be enabled (1).
  958.  
  959.  
  960.           AT_BIOS
  961.                If enabled, the AT BIOS wait/post handler is installed. 
  962.                May be disabled when compatibility problems arise.
  963.  
  964.                This option is normally enabled (1).
  965.                
  966.  
  967.                                   Memory Allocation
  968.  
  969.           TSKALLOC.C  is  needed if TSK_DYNAMIC is enabled in  tskconf.h  to 
  970.           handle  the allocation and free calls. If you want to use  dynamic 
  971.           allocation  in your own tasks, you should also use  the  functions 
  972.           tsk_alloc and tsk_free to avoid the reentrancy problems  mentioned 
  973.           in the introduction. If you should need other forms of alloc (like 
  974.           calloc)  the  recommended  way  is  to  add  those  functions   to 
  975.           TSKALLOC.C, requesting the resource alloc_resource before  calling 
  976.           the C memory-allocation function.
  977.  
  978.  
  979.                                        Snapshot
  980.  
  981.           TSKSNAP.C is only needed if you want to include the snapshot  dump 
  982.           into  your  program. Note that you can *not* use snapshot  if  you 
  983.           disable TSK_NAMED in tskconf.h.
  984.  
  985.  
  986.                                       Task Stacks
  987.  
  988.           When  compiling your application,  turn stack  checking  off.  The 
  989.           standard  stack check is of little use with task stacks,  and  may 
  990.           interfere with CTask's operation.  The stack area for tasks should 
  991.           be  allocated  on the main program's stack,  not in the static  or 
  992.           heap data space. The reason for this is that some of the C library 
  993.           routines check for stack overflow regardless of your  compile-time 
  994.           switches,  and  will  crash  your application if  you  use  stacks 
  995.           outside the normal stack. The stack allocation parameter with LINK 
  996.           (MS-C),  or the _stacksize variable (Turbo C) have to be increased 
  997.           to reflect the additional stack space. When calculating task stack 
  998.           sizes, keep in mind that library routines (esp. printf) allocate a 
  999.           lot of space on the stack for temporary variables. A minimum of 1k 
  1000.           for  tasks using library routines is recommended,  2k puts you  on 
  1001.           the  safe  side.  Tasks  not using C library routines  may  use  a 
  1002.           smaller  stack,  about 256 bytes at a minimum,  plus space for any 
  1003.           CTask Manual          Version 1.1  88-07-01                Page 19
  1004.  
  1005.  
  1006.  
  1007.           local variables and nested routines. Then add up all task  stacks, 
  1008.           and   add   space  for  the  main  task  (the   function   calling 
  1009.           install_tasker), with this size also dependent on what you will do 
  1010.           in  the main task while CTask is active. Stacks for tasks that  do 
  1011.           not use C library routines may be allocated anywhere.
  1012.  
  1013.  
  1014.                                         Drivers
  1015.  
  1016.           The  keyboard  and DOS handlers are always installed  with  CTask. 
  1017.           Using the serial I/O and printer drivers is optional,  so you have 
  1018.           to  install them separately,  but only *after*  installing  CTask. 
  1019.           When  using  the serial driver,  include "sio.h" in your  modules, 
  1020.           when using the printer driver, include "prt.h".
  1021.  
  1022.           Another  driver  that  is  automatically  installed  is  the  BIOS 
  1023.           wait/post  handler  for  the IBM AT. The  AT  BIOS  contains  some 
  1024.           multitasking  hooks  to  avoid busy waiting in the  BIOS.  If  you 
  1025.           experience  problems with disk accesses or printer output  through 
  1026.           BIOS  (not through the CTask printer driver), you  should  disable 
  1027.           installation  of  this  driver  by  setting  AT_BIOS  to  zero  in 
  1028.           tskconf.h.  Normally,  no problems should arise with  this  driver 
  1029.           even in XT type machines.
  1030.  
  1031.  
  1032.                                    Things to remember
  1033.  
  1034.           Remember that tasks are not automatically started after  creation. 
  1035.           Use start_task to allow a created task to run.
  1036.  
  1037.           Always use create_xxx on resources,  pipes,  etc.  Using the event 
  1038.           routines without doing so will have unpredictable results.
  1039.  
  1040.           Before exiting the program, all installed drivers and CTask should
  1041.           be  explicitly  removed.  Although  the  DOS  handler  traps   the 
  1042.           terminate  call and automatically calls remove_tasker, you  should 
  1043.           make  sure  that  all  tasks  are  completed  properly,  and  call 
  1044.           remove_tasker yourself.
  1045.  
  1046.           Deleting events before exiting the program is not  mandatory,  but 
  1047.           recommended to kill all tasks waiting for the event. You should be 
  1048.           careful  not  to  kill tasks while they are  active  in  DOS.  The 
  1049.           kill_task routine should be reserved for fatal error handling. The 
  1050.           best  way  is to let the tasks kill themselves depending  on  some 
  1051.           global  variable or event.  If a task is killed while waiting  for 
  1052.           input  in DOS, DOS operation may be severely impaired. If you  use 
  1053.           the C console input routines, make sure that the task returns from 
  1054.           DOS  before it is killed, if necessary by requesting the  user  to 
  1055.           press a key.
  1056.           CTask Manual          Version 1.1  88-07-01                Page 20
  1057.  
  1058.  
  1059.  
  1060.                                   Priority Handling
  1061.                                   =================
  1062.  
  1063.           CTask   provides   for  prioritized  task  execution   and   event 
  1064.           processing. This means that a task that has a higher priority will 
  1065.           be  run  before any other tasks having  lower  priority.  Also,  a 
  1066.           higher  priority  task will gain access  to  resources,  counters, 
  1067.           pipes,   and  mail,   before  lower  priority  tasks.  With  fixed 
  1068.           priorities,  this  means that a high priority task can  monopolize 
  1069.           CPU  time,  even  if  it  calls  schedule()  explicitly.  Variable 
  1070.           priority  increases each eligible task's priority by one  on  each 
  1071.           scheduler  call,  so that lower priority tasks will slowly rise to 
  1072.           the head of the queue until they get executed.  The priority  will 
  1073.           be reset to the initial priority when a task is run.
  1074.  
  1075.           Since  variable priority increases processing time in the critical 
  1076.           region  of  the scheduler,  it is not recommended for  systems  in 
  1077.           which  a  larger  number  of tasks  is  expected  to  be  eligible 
  1078.           simultaneously.
  1079.  
  1080.           Usually,  all  tasks  in a system should have the  same  priority, 
  1081.           with   only  very  few  exceptions  for  non-critical   background 
  1082.           processing  (low  priority)  or  very  time-critical  tasks  (high 
  1083.           priority).  High  priority  tasks should be written in such a  way 
  1084.           that  they  either  reduce  their  priority  when  processing   is 
  1085.           completed,  or that they wait for an event. Busy waiting in a high 
  1086.           priority  task will severely impair system operation with variable 
  1087.           priority  enabled,  and  will stop the system until  the  task  is 
  1088.           placed in a waiting state with fixed priority.
  1089.  
  1090.           The  (automatically created) main task is started with the highest 
  1091.           possible priority below the timer task, so that it can process all 
  1092.           initialisations  before other tasks start running.  To  allow  the 
  1093.           system to operate,  the priority of the main task must be reduced, 
  1094.           or the main task must wait for an event or a timeout.
  1095.           CTask Manual          Version 1.1  88-07-01                Page 21
  1096.  
  1097.  
  1098.  
  1099.                                    CTask Data Types
  1100.                                    ================
  1101.  
  1102.           Note  that you do not have to know the innards of the  structures. 
  1103.           All  structure fields are filled by the "create_xxx" routines  and 
  1104.           modified  by the CTask functions.  You should NEVER modify a field 
  1105.           in  one of the structures directly.  The structures are  explained 
  1106.           here shortly only for those wanting to modify the routines. 
  1107.  
  1108.           If  you only want to use the routines,  you should simply  include 
  1109.           the file "tsk.h" in your source, and define variables of the types
  1110.  
  1111.                 tcb         - for task control blocks
  1112.                 tcbptr      - for far pointers to tcbs
  1113.  
  1114.                 flag        - for flag events
  1115.                 flagptr     - for far pointers to flags
  1116.  
  1117.                 resource    - for resource events
  1118.                 resourceptr - for far pointers to resources
  1119.  
  1120.                 counter     - for counter events
  1121.                 counterptr  - for far pointers to counters
  1122.  
  1123.                 mailbox     - for mailbox events
  1124.                 mailboxptr  - for far pointers to mailboxes
  1125.  
  1126.                 pipe        - for pipe events
  1127.                 pipeptr     - for far pointers to pipes
  1128.  
  1129.                 wpipe       - for word pipe events
  1130.                 wpipeptr    - for far pointers to word pipes
  1131.  
  1132.                 buffer      - for buffer events
  1133.                 bufferptr   - for far pointers to buffers
  1134.  
  1135.                 tlink       - for timeout control blocks
  1136.                 tlinkptr    - for far pointers to timeout control blocks
  1137.  
  1138.                 namerec     - for control block names
  1139.                 nameptr     - for name pointers
  1140.  
  1141.           without caring what's behind them.
  1142.  
  1143.           Additionally, you may use the types
  1144.  
  1145.                 byte        - for unsigned characters
  1146.                 word        - for unsigned short integers
  1147.                 dword       - for unsigned long integers
  1148.  
  1149.                 funcptr     - for pointers to task functions
  1150.                 farptr      - for far pointers to anything
  1151.                 byteptr     - for far pointers to byte arrays
  1152.                 wordptr     - for far pointers to word arrays
  1153.           CTask Manual          Version 1.1  88-07-01                Page 22
  1154.  
  1155.  
  1156.  
  1157.  
  1158.           in  defining  or typecasting items to be passed as  parameters  to 
  1159.           CTask functions.
  1160.  
  1161.  
  1162.                   Typedefs used for simplified type specifications
  1163.  
  1164.              typedef unsigned char byte;
  1165.              typedef unsigned short word;
  1166.              typedef unsigned long dword;
  1167.              typedef void (cdecl far *funcptr)();
  1168.              typedef void far *farptr;
  1169.              typedef byte far *byteptr;
  1170.              typedef word far *wordptr;
  1171.  
  1172.                       Error return values for mailbox functions
  1173.  
  1174.              #define TTIMEOUT ((farptr) -1L)
  1175.                This value is returned if a timeout occurred during a mailbox 
  1176.                wait.
  1177.  
  1178.              #define TWAKE    ((farptr) -2L)
  1179.                This  value  is returned if the task was waked  up  during  a 
  1180.                mailbox wait.
  1181.  
  1182.  
  1183.                                The timer control block
  1184.  
  1185.           The  timer control block is included in every task control  block. 
  1186.           It  may  also be created as a separate entity to  specify  special 
  1187.           actions  to be executed after or every n timer ticks.  The  "next" 
  1188.           field links the active structures into the timer queue.
  1189.  
  1190.           "timeout"  gives the number of timer ticks, "reload" is  used  for 
  1191.           repetitive  timeout actions to reload the original timeout  value. 
  1192.  
  1193.           "strucp"  points  to  the  structure to  be  acted  upon,  "tkind" 
  1194.           specifies the kind of structure:
  1195.  
  1196.                #define  TKIND_TASK        1
  1197.                     Strucp  points  to the task control block of  which  the 
  1198.                     element is a member. The task will be awakened when  the 
  1199.                     timeout expires.
  1200.  
  1201.                #define  TKIND_WAKE        2
  1202.                     Strucp points to a task control block. Otherwise same as 
  1203.                     TKIND_TASK.
  1204.  
  1205.                #define  TKIND_PROC        3
  1206.                     Strucp  contains the address of a function to be  called 
  1207.                     on timeout.
  1208.  
  1209.                #define  TKIND_FLAG        4
  1210.                     Strucp points to a flag that should be set on timeout.
  1211.           CTask Manual          Version 1.1  88-07-01                Page 23
  1212.  
  1213.  
  1214.  
  1215.  
  1216.                #define  TKIND_COUNTER     5
  1217.                     Strucp  points to a counter that should be increased  on 
  1218.                     timeout.
  1219.  
  1220.                #define  TKIND_TEMP        0x80
  1221.                     This  is  a modifying flag. If set,  the  timer  control 
  1222.                     block was allocated dynamically, and should be freed  on 
  1223.                     timeout.
  1224.  
  1225.  
  1226.           "tstate" contains the state of the element:
  1227.  
  1228.                #define  TSTAT_REMOVE      -1
  1229.                     The  element should be taken off the queue on  the  next 
  1230.                     pass (if the TKIND_TEMP-flag is set, it will be freed).
  1231.  
  1232.                #define  TSTAT_IDLE        0
  1233.                     The element is not enqueued.
  1234.  
  1235.                #define  TSTAT_COUNTDOWN   1
  1236.                     The  timeout counter is counted down. If the timeout  is 
  1237.                     reached, the element is removed from the queue.
  1238.  
  1239.                #define  TSTAT_REPEAT      2
  1240.                     The  timeout counter is counted down. If the timeout  is 
  1241.                     reached, the timeout count is reloaded.
  1242.  
  1243.  
  1244.              typedef struct tlink_rec far *tlinkptr;
  1245.  
  1246.              struct tlink_rec {
  1247.                               tlinkptr next;
  1248.                               dword    timeout;
  1249.                               dword    reload;
  1250.                               farptr   strucp;
  1251.                               byte     tstate;
  1252.                               byte     tkind;
  1253.                               };
  1254.  
  1255.              typedef struct tlink_rec tlink;
  1256.  
  1257.  
  1258.  
  1259.                                The name link structure
  1260.  
  1261.           If  TSK_NAMED is enabled, all structures except the timer  control 
  1262.           block contain a name link. All control blocks are linked and named 
  1263.           via  this  element.  To facilitate removal from the  list,  it  is 
  1264.           doubly  linked  via  the pointers "follow"  and  "prev".  
  1265.  
  1266.           "strucp"  points to the head of the structure the name link is  an 
  1267.           element of, with "nkind" specifying the type of structure:
  1268.  
  1269.           CTask Manual          Version 1.1  88-07-01                Page 24
  1270.  
  1271.  
  1272.  
  1273.                #define  TYP_TCB        1     task control block
  1274.                #define  TYP_FLAG       2     flag event
  1275.                #define  TYP_RESOURCE   3     resource event
  1276.                #define  TYP_COUNTER    4     counter event
  1277.                #define  TYP_MAILBOX    5     mailbox event
  1278.                #define  TYP_PIPE       6     byte pipe
  1279.                #define  TYP_WPIPE      7     word pipe
  1280.                #define  TYP_BUFFER     8     buffer
  1281.  
  1282.           The head element of the name list has its nkind field set to zero.
  1283.  
  1284.           The  "name" field contains an up to 8-character name plus  a  zero 
  1285.           terminator.
  1286.  
  1287.                #define  NAMELENGTH     9
  1288.  
  1289.                typedef struct name_rec far *nameptr;
  1290.  
  1291.                struct name_rec {
  1292.                                nameptr    follow;    
  1293.                                nameptr    prev;      
  1294.                                farptr     strucp;    
  1295.                                byte       nkind;     
  1296.                                char       name [NAMELENGTH];
  1297.                                };
  1298.  
  1299.                typedef struct name_rec namerec;
  1300.  
  1301.  
  1302.  
  1303.                           The task control block structure
  1304.  
  1305.           The  "next" field points to the next tcb in a queue.  The  "queue" 
  1306.           pointer  points to the head of the queue the task is enqueued  in, 
  1307.           or will be enqueued in on the next schedule request in the case of 
  1308.           the current running task. 
  1309.  
  1310.           "stack" contains the saved task stack pointer (offset and segment) 
  1311.           if the task is not running. The field "stkbot" contains the bottom 
  1312.           address  of  the task stack.  It is set  by  create_task,  but  is 
  1313.           currently  not used anywhere else.  Stack checking routines  might 
  1314.           use this value to test for stack overflow and/or stack usage.
  1315.  
  1316.           "prior"  contains  the tasks current  priority,  with  0xffff  the 
  1317.           highest possible priority, and 0 the lowest. "initprior" initially 
  1318.           contains the same value.  If variable priority is enabled, "prior" 
  1319.           is  incremented on each scheduler call,  and reset to  "initprior" 
  1320.           when the task is activated.
  1321.  
  1322.           "state" and "flags" contain the tasks state and flags.
  1323.  
  1324.           "timerq" is a tlink structure used to chain the tcb into the timer 
  1325.           queue,  if  the  task is waiting for a timeout. See  above  for  a 
  1326.           description of the tlink structure.
  1327.           CTask Manual          Version 1.1  88-07-01                Page 25
  1328.  
  1329.  
  1330.  
  1331.  
  1332.           The fields "retptr" and "retsize" are used in event handling. They 
  1333.           are  used  when a task is waiting for an event by the  task  acti-
  1334.           vating the event,  and also by timeout and wake to indicate  error 
  1335.           returns.   The  use of these pointers eliminates the need to  loop 
  1336.           for  an  event,  which  requires slightly more code in  the  event 
  1337.           handling routines, but reduces the need for task switching.
  1338.  
  1339.           The  "name"  namerec  structure is present only  if  TSK_NAMED  is 
  1340.           enabled.  It  may  be  used in debugging  (see  tsksnap.c  for  an 
  1341.           example),  and in applications where the address of the  structure 
  1342.           can not be passed directly.
  1343.  
  1344.              typedef struct tcb_rec far *tcbptr;
  1345.              typedef tcbptr far *tqueptr;
  1346.  
  1347.              struct tcb_rec {
  1348.                              tcbptr   next;
  1349.                              tqueptr  queue;
  1350.                              byteptr  stack;
  1351.                              byteptr  stkbot;
  1352.                              word     prior;
  1353.                              word     initprior;
  1354.                              byte     state;
  1355.                              byte     flags;
  1356.                              dlink    timerq;
  1357.                              farptr   retptr;
  1358.                              int      retsize;
  1359.                             };
  1360.  
  1361.              typedef struct tcb_rec tcb;
  1362.  
  1363.  
  1364.                                      Task states
  1365.  
  1366.              #define  ST_KILLED   0
  1367.              #define  ST_STOPPED  1
  1368.              #define  ST_DELAYED  2
  1369.              #define  ST_WAITING  3
  1370.              #define  ST_ELIGIBLE 4
  1371.              #define  ST_RUNNING  5
  1372.  
  1373.           CTask Manual          Version 1.1  88-07-01                Page 26
  1374.  
  1375.  
  1376.  
  1377.                      Possible task states and queue association
  1378.  
  1379.           ST_KILLED      The  task has been killed.  Restarting the task  is 
  1380.                          not possible. Queue pointers are invalid.
  1381.  
  1382.           ST_STOPPED     The  task is not enqueued in any queue.  To be  in-
  1383.                          cluded  in  scheduling,  it  has to  be  explicitly 
  1384.                          started. The queue head pointer is NULL.
  1385.  
  1386.           ST_DELAYED     The task is enqueued in the timer queue only.  When 
  1387.                          the  timer expires,  it is placed in  the  eligible 
  1388.                          queue.  The queue head pointer is NULL.
  1389.  
  1390.           ST_ELIGIBLE    The  task  is  enqueued in the queue  of  processes 
  1391.                          eligible for running.  It can not be chained in the 
  1392.                          timer queue.  The queue head pointer points to  the 
  1393.                          eligible queue.
  1394.  
  1395.           ST_RUNNING     The task is the current running process.   Although 
  1396.                          it  is  not enqueued in any queue,  the queue  head 
  1397.                          pointer in its control block is valid and points to 
  1398.                          the  queue the process will be enqueued in  on  the 
  1399.                          next schedule request.
  1400.  
  1401.           ST_WAITING     The task is waiting for an event to happen.  It can 
  1402.                          also  be chained into the timer queue if a  timeout 
  1403.                          was specified in the call.   The queue head pointer 
  1404.                          points to the queue head in the event control block 
  1405.                          for the event the process is waiting on.
  1406.  
  1407.  
  1408.                     Possible state transitions and their reasons
  1409.  
  1410.              stopped  -> eligible    by start_task ()
  1411.  
  1412.              delayed  -> killed      by kill_task ()
  1413.                       -> eligible    by timer task, or wake_task ()
  1414.              
  1415.              eligible -> killed      by kill_task ()
  1416.                       -> running     by scheduler
  1417.  
  1418.              running  -> killed      by kill_task ()
  1419.                       -> stopped     by delay (0)
  1420.                       -> delayed     by delay (n != 0)
  1421.                       -> eligible    by scheduler
  1422.                       -> waiting     by wait_xxx ()
  1423.  
  1424.              waiting  -> killed      by kill_task ()
  1425.                       -> eligible    by event happening, timeout,
  1426.                                      or wake_task()
  1427.  
  1428.  
  1429.           CTask Manual          Version 1.1  88-07-01                Page 27
  1430.  
  1431.  
  1432.  
  1433.                                      Task flags
  1434.  
  1435.           System flags:
  1436.  
  1437.                #define  F_TEMP   0x80
  1438.                     This tcb was allocated automatically, and must be free'd 
  1439.                     on task kill.
  1440.  
  1441.                #define  F_STTEMP 0x40
  1442.                     The tasks stack was allocated automatically, and must be 
  1443.                     free'd on task kill.
  1444.  
  1445.           User changeable flags:
  1446.  
  1447.                #define  F_CRIT   0x01
  1448.                     This  task  may not be preempted. It will run  until  it 
  1449.                     clears  this  flag, delays itself, calls  the  scheduler 
  1450.                     explicitly, or waits for an event.
  1451.  
  1452.  
  1453.  
  1454.                                The event control blocks
  1455.  
  1456.           All event control blocks have two optional fields:
  1457.  
  1458.           "name"  is only present if TSK_NAMED is enabled. It is  a  namerec 
  1459.           structure as explained above.
  1460.  
  1461.           "flags" is only present if TSK_DYNAMIC is enabled. It contains
  1462.  
  1463.                F_TEMP    If this control block was allocated  automatically, 
  1464.                          and must be free'd on delete.
  1465.  
  1466.                F_STTEMP  If  the buffer for this event was  allocated  auto-
  1467.                          matically, and must be free'd on delete (pipes  and 
  1468.                          buffers only).
  1469.  
  1470.  
  1471.  
  1472.           CTask Manual          Version 1.1  88-07-01                Page 28
  1473.  
  1474.  
  1475.  
  1476.                               The flag event structure
  1477.  
  1478.           Contains  two queues for processes waiting on a flag state  (clear 
  1479.           or set), plus the flag state (0 = clear, 1 = set).
  1480.  
  1481.              typedef struct {
  1482.                             tcbptr  wait_set;
  1483.                             tcbptr  wait_clear;
  1484.                             int     state;
  1485.                             byte    flags;   (if TSK_DYNAMIC)
  1486.                             namerec name;    (if TSK_NAMED)
  1487.                             } flag;
  1488.  
  1489.              typedef flag far *flagptr;
  1490.  
  1491.  
  1492.                              The counter event structure
  1493.  
  1494.           Similar to a flag, but contains a doubleword state counter.
  1495.  
  1496.              typedef struct {
  1497.                             tcbptr  wait_set;
  1498.                             tcbptr  wait_clear;
  1499.                             dword   state;
  1500.                             byte    flags;   (if TSK_DYNAMIC)
  1501.                             namerec name;    (if TSK_NAMED)
  1502.                             } counter;
  1503.  
  1504.              typedef counter far *counterptr;
  1505.  
  1506.  
  1507.                             The resource event structure
  1508.  
  1509.           Contains a queue for the tasks waiting for access to the resource, 
  1510.           a  pointer  to  the current owner of the resource  (to  check  for 
  1511.           illegal "release_resource" calls),  and the resource state (0 = in 
  1512.           use, 1 = free).
  1513.  
  1514.              typedef struct {
  1515.                             tcbptr   waiting;
  1516.                             tcbptr   owner;
  1517.                             int      state;
  1518.                             byte     flags;   (if TSK_DYNAMIC)
  1519.                             namerec  name;    (if TSK_NAMED)
  1520.                             } resource;
  1521.  
  1522.              typedef resource far *resourceptr;
  1523.  
  1524.  
  1525.           CTask Manual          Version 1.1  88-07-01                Page 29
  1526.  
  1527.  
  1528.  
  1529.                              The mailbox event structure
  1530.  
  1531.           The msgptr type is only used internally to chain mail blocks  into 
  1532.           the  mailbox.  The  mailbox  type contains a queue  of  the  tasks 
  1533.           waiting  for mail,  and a first and last pointer for the chain  of 
  1534.           mail blocks.  
  1535.  
  1536.              struct msg_header {
  1537.                                struct msg_header far *next;
  1538.                                };
  1539.  
  1540.              typedef struct msg_header far *msgptr;
  1541.  
  1542.              typedef struct {
  1543.                             tcbptr  waiting;
  1544.                             msgptr  mail_first;
  1545.                             msgptr  mail_last;
  1546.                             byte    flags;   (if TSK_DYNAMIC)
  1547.                             namerec name;    (if TSK_NAMED)
  1548.                             } mailbox;
  1549.  
  1550.              typedef mailbox far *mailboxptr;
  1551.  
  1552.  
  1553.                         The pipe and word pipe event structure
  1554.  
  1555.           Contains queues of the tasks waiting to read or write to the pipe, 
  1556.           indices  for reading (outptr) and writing (inptr) into the buffer, 
  1557.           the  buffer size,  the number of bytes or words currently  in  the 
  1558.           pipe  ("filled"),  and the pointer to the buffer.  A word pipe  is 
  1559.           handled exactly the same as a byte pipe, the only difference being 
  1560.           the element size placed in the buffer.  With normal pipes, charac-
  1561.           ters are buffered, with word pipes, words.
  1562.  
  1563.              typedef struct {
  1564.                             tcbptr   wait_read;
  1565.                             tcbptr   wait_write;
  1566.                             tcbptr   wait_clear;
  1567.                             word     bufsize;
  1568.                             word     filled;
  1569.                             word     inptr;
  1570.                             word     outptr;
  1571.                             byteptr  contents;
  1572.                             byte    flags;   (if TSK_DYNAMIC)
  1573.                             namerec name;    (if TSK_NAMED)
  1574.                             } pipe;
  1575.  
  1576.              typedef pipe far *pipeptr;
  1577.  
  1578.           CTask Manual          Version 1.1  88-07-01                Page 30
  1579.  
  1580.  
  1581.  
  1582.              typedef struct {
  1583.                             tcbptr   wait_read;
  1584.                             tcbptr   wait_write;
  1585.                             tcbptr   wait_clear;
  1586.                             word     bufsize;
  1587.                             word     filled;
  1588.                             word     inptr;
  1589.                             word     outptr;
  1590.                             wordptr  wcontents;
  1591.                             byte    flags;   (if TSK_DYNAMIC)
  1592.                             namerec name;    (if TSK_NAMED)
  1593.                             } wpipe;
  1594.  
  1595.              typedef wpipe far *wpipeptr;
  1596.  
  1597.  
  1598.                               The buffer event structure
  1599.  
  1600.           Contains  resources for read and write access, the word pipe  used 
  1601.           for buffering, and a message counter.
  1602.  
  1603.              typedef struct {
  1604.                              resource    buf_write;
  1605.                              resource    buf_read;
  1606.                              wpipe       pip;
  1607.                              word        msgcnt;
  1608.                              byte        flags;   (if TSK_DYNAMIC)
  1609.                              namerec     name;    (if TSK_NAMED)
  1610.                             } buffer;
  1611.  
  1612.              typedef buffer far *bufferptr;
  1613.  
  1614.  
  1615.  
  1616.           NOTE:  When  modifying CTask structures,  take care to modify  the 
  1617.           equivalent  definitions  in the assembler include file  "tsk.mac". 
  1618.           Some  of  the assembler routines have to use  field  offsets  into 
  1619.           pointers,  so  having  different offsets in C and  assembler  will 
  1620.           crash the system.
  1621.           CTask Manual          Version 1.1  88-07-01                Page 31
  1622.  
  1623.  
  1624.  
  1625.                                    CTask Routines
  1626.                                    ==============
  1627.  
  1628.                               Installation and Removal
  1629.  
  1630.           void install_tasker (int varpri, int speedup);
  1631.  
  1632.                Installs  the multitasker.  Must be called prior to any other 
  1633.                routine. The calling routine is defined as the main task, and 
  1634.                assigned  the  highest priority.   To allow  other  tasks  to 
  1635.                execute,  the  main task must have its priority  reduced,  be 
  1636.                delayed,  or  wait  on an event.   
  1637.  
  1638.                The "varpri" parameter enables variable priority if nonzero.
  1639.  
  1640.                The  "speedup" parameter is defined for the IBM  PC/XT/AT  as 
  1641.                the clock tick speedup factor. The timer tick frequency  will 
  1642.                be set to
  1643.  
  1644.                     speedup  ticks/sec   msecs/tick
  1645.                       0        18.2        54.9     (normal clock)
  1646.                       1        36.4        27.5
  1647.                       2        72.8        13.7
  1648.                       3       145.6         6.9
  1649.                       4       291.3         3.4
  1650.  
  1651.                Note  that  all  timeouts are specified  in  tick  units,  so 
  1652.                changing  the speedup parameter will influence  timeouts  and 
  1653.                delays.  You  can enable CLOCK_MSECS in  tskconf.h  to  allow 
  1654.                timeouts to be specified in milliseconds.
  1655.                The system clock will not be disturbed by changing the speed. 
  1656.                Using values above 2 can lead to interrupt overruns on slower 
  1657.                machines and is not recommended.
  1658.  
  1659.  
  1660.           void remove_tasker (void);
  1661.  
  1662.                Uninstalls the multitasker. Must only be called from the main 
  1663.                task, and should be called before exiting the program.
  1664.  
  1665.  
  1666.  
  1667.                                     Miscellaneous
  1668.  
  1669.  
  1670.           void preempt_off (void);
  1671.  
  1672.                Disables task preemption. This is the default after installa-
  1673.                tion.  With preemption turned off,  only delays, event waits, 
  1674.                and explicit scheduler calls will cause a task switch.
  1675.  
  1676.  
  1677.           CTask Manual          Version 1.1  88-07-01                Page 32
  1678.  
  1679.  
  1680.  
  1681.           void preempt_on (void);
  1682.  
  1683.                Enables  task preemption.  Task switches will occur on  timer 
  1684.                ticks.
  1685.  
  1686.  
  1687.           void far schedule (void);
  1688.  
  1689.                Explicit  scheduling request.  The highest priority  eligible 
  1690.                task will be made running.
  1691.  
  1692.  
  1693.           void far c_schedule (void);
  1694.  
  1695.                Conditional  scheduling request.  Scheduling will take  place 
  1696.                only if preemption is allowed.
  1697.  
  1698.  
  1699.           void tsk_dis_preempt (void)
  1700.  
  1701.                Temporarily  disable task preemption.  Preemption will be re-
  1702.                enabled  by  tsk_ena_preempt or  an  unconditional  scheduler 
  1703.                call.
  1704.  
  1705.  
  1706.           void tsk_ena_preempt (void)
  1707.  
  1708.                Re-enable  task  preemption.  Note that  tsk_dis_preempt  and 
  1709.                tsk_ena_preempt do not change the global preemption state set 
  1710.                by preempt_off and preempt_on.
  1711.  
  1712.  
  1713.           int tsk_dis_int (void)
  1714.  
  1715.                Disable  interrupts.  Returns the state of the interrupt flag 
  1716.                prior to this call (1 if interrupts were enabled).
  1717.  
  1718.  
  1719.           void tsk_ena_int (int state)
  1720.  
  1721.                Enables  interrupts if "state" is nonzero.  Normally used  in 
  1722.                conjunction with tsk_dis_int.
  1723.  
  1724.           The routines tsk_dis_int and tsk_ena_int may be used in a  simpli-
  1725.           fied scheme with the defines
  1726.  
  1727.                CRITICAL;      Declares "int crit_intsav;".
  1728.                C_ENTER;       Expands to "crit_intsav = tsk_dis_int ();"
  1729.                C_LEAVE;       Expands to "tsk_ena_int (crit_intsav);".
  1730.  
  1731.  
  1732.           void tsk_cli (void)
  1733.  
  1734.                Disables interrupts (intrinsic function).
  1735.           CTask Manual          Version 1.1  88-07-01                Page 33
  1736.  
  1737.  
  1738.  
  1739.  
  1740.  
  1741.           void tsk_sti (void)
  1742.  
  1743.                Unconditionally enables interrupts (intrinsic function).
  1744.  
  1745.  
  1746.           void tsk_outp (int port, byte b)
  1747.  
  1748.                Outputs  the  value "b" to  hardware-port  "port"  (intrinsic 
  1749.                function).
  1750.  
  1751.           byte tsk_inp (int port)
  1752.  
  1753.                Returns the value read from port "port" (intrinsic function).
  1754.  
  1755.  
  1756.           The following entry points may be used from assembler routines:
  1757.  
  1758.           extrn scheduler: far
  1759.  
  1760.                Direct entry into the scheduler.  The stack must be set up as 
  1761.                for an interrupt handler, e.g.
  1762.                     pushf
  1763.                     cli
  1764.                     call  scheduler
  1765.  
  1766.  
  1767.           extrn _sched_int: far
  1768.  
  1769.                Conditional scheduling call.  The stack must be set up as for 
  1770.                an interrupt handler.
  1771.  
  1772.  
  1773.  
  1774.           CTask Manual          Version 1.1  88-07-01                Page 34
  1775.  
  1776.  
  1777.  
  1778.                                    Task Operations
  1779.  
  1780.           tcbptr create_task (tcbptr task, funcptr func, byteptr stack,
  1781.                               word stksz, word prior, farptr arg 
  1782.                               [, byteptr name]);
  1783.  
  1784.                Initialises a task.  Must be called prior to any other opera-
  1785.                tions  on this task.  The task is in the stopped state  after 
  1786.                creation. It must be started to be able to run.
  1787.  
  1788.                "task"    is  a  pointer to a tcb (NULL for  automatic  allo-
  1789.                          cation).
  1790.                "func"    is a pointer to a void far function, with a single 
  1791.                          dword sized parameter.
  1792.                "stack"   is a pointer to a stack area for the task (NULL for 
  1793.                          automatic allocation).
  1794.                "stksz"   is the size of the stack area in bytes.
  1795.                "prior"   is the tasks priority (0 lowest, 0xffff highest).
  1796.                "arg"     is an argument to the created task. It may be used 
  1797.                          to differentiate between tasks when using the same 
  1798.                          function in different tasks.
  1799.                "name"    is  the name of the task,  up to eight  characters, 
  1800.                          plus  zero-terminator.  This  parameter is  defined 
  1801.                          only if TSK_NAMEPAR is enabled in tskconf.h.
  1802.  
  1803.  
  1804.           void kill_task (tcbptr task);
  1805.  
  1806.                Kills a task. The task can no longer be used.
  1807.  
  1808.  
  1809.           int start_task (tcbptr task);
  1810.  
  1811.                Starts a task, i.e. makes it eligible for running.
  1812.                Returns  0  if  task  was started,  -1 if the  task  was  not 
  1813.                stopped.
  1814.                A value of NULL for "task" will start the "main task".
  1815.  
  1816.  
  1817.           int wake_task (tcbptr task);
  1818.  
  1819.                Prematurely wakes a delayed or waiting task.
  1820.                If the task was waiting for an event, it will be removed from 
  1821.                the  waiting queue,  with the operation terminating  with  an 
  1822.                error return value.
  1823.                Returns  0 if task was waked,  -1 if the task was not delayed 
  1824.                or waiting.
  1825.                A value of NULL for "task" will wake the "main task".
  1826.  
  1827.  
  1828.           CTask Manual          Version 1.1  88-07-01                Page 35
  1829.  
  1830.  
  1831.  
  1832.           void set_priority (tcbptr task, word prior)
  1833.  
  1834.                Sets the priority of the specified task to "prior".
  1835.                Note that you should NOT modify the priority field in the tcb 
  1836.                structure directly.
  1837.                A value of NULL for "task" will set the priority of the "main 
  1838.                task".
  1839.  
  1840.  
  1841.           void set_task_flags (tcbptr task, byte flags)
  1842.  
  1843.                Sets the flags of the task to "flags".  Currently,  the  only 
  1844.                flag that can be changed is
  1845.  
  1846.                          F_CRIT   the task can not be preempted if set.
  1847.  
  1848.                Note  that  you  may  NOT modify the flag field  in  the  tcb 
  1849.                structure directly.
  1850.                A  value of NULL for "task" will set the flags of  the  "main 
  1851.                task".
  1852.  
  1853.  
  1854.                                   Timer Operations
  1855.  
  1856.           Timeouts are normally specified in timer ticks. If CLOCK_MSECS  is 
  1857.           enabled, timeouts will be converted to the nearest number of timer 
  1858.           ticks  automatically.  The global  word  variable  "ticks_per_sec" 
  1859.           contains  a rough estimate of the number of ticks per second  even 
  1860.           if CLOCK_MSECS is not enabled.
  1861.  
  1862.  
  1863.                                  Event wait Timeouts
  1864.  
  1865.           When  waiting  for  an event,  a timeout may  be  specified.   The 
  1866.           operation will terminate with an error return value if the timeout 
  1867.           is reached before the event occurs.
  1868.           NOTE  that the timeout parameter is not optional.  To  specify  no 
  1869.           timeout, use the value 0.
  1870.  
  1871.  
  1872.                                        Delays
  1873.  
  1874.           int t_delay (dword ticks);
  1875.  
  1876.                Delay the current task for "ticks" clock ticks/milliseconds.
  1877.                If ticks is 0, the task is stopped.
  1878.  
  1879.  
  1880.                                     Timed Events
  1881.  
  1882.           You can create timer control blocks to
  1883.  
  1884.                - set a flag
  1885.                - increment a counter
  1886.           CTask Manual          Version 1.1  88-07-01                Page 36
  1887.  
  1888.  
  1889.  
  1890.                - wake up a task
  1891.                - call a function
  1892.  
  1893.           after  a specified timeout interval.  The operation may optionally 
  1894.           be  repeated.  For calling functions on a timeout,  the  following 
  1895.           must be noted:
  1896.  
  1897.                Timeout functions should be as short as possible, since they
  1898.                run at the highest priority.
  1899.                The  stack and data area is the area of the  timer  task.  Be 
  1900.                careful when referencing variables.
  1901.                Timeout  functions may not use any functions that could cause 
  1902.                the timer task to be made waiting.
  1903.  
  1904.  
  1905.           tlinkptr create_timer (tlinkptr elem, dword tout, farptr strucp,
  1906.                                  byte kind, byte rept);
  1907.  
  1908.                Create a timer control block.
  1909.                Returns the address of the control block, NULL on error.
  1910.  
  1911.                "elem"    is the control block to initialise (NULL for auto-
  1912.                          matic allocation).
  1913.                "tout"    specifies the timeout interval.
  1914.                "strucp"  points  to  the structure to be  used.  This  is  a 
  1915.                          flagptr  for  setting  a  flag,  a  counterptr  for 
  1916.                          increasing counters, a tcbptr for waking a task, or 
  1917.                          a funcptr for calling a function.
  1918.                "kind"    gives the kind of structure "strucp" points to.  It 
  1919.                          must be one of
  1920.                               TKIND_WAKE     for task-wakeup
  1921.                               TKIND_PROC     for function call
  1922.                               TKIND_FLAG     for flag set
  1923.                               TKIND_COUNTER  for counter increment.
  1924.                "rept"    if  nonzero,  the action will be repeated on  every 
  1925.                          "tout" timeout interval.
  1926.  
  1927.                NOTE: Timer control blocks can not be named.
  1928.                
  1929.  
  1930.           void delete_timer (tlinkptr elem);
  1931.  
  1932.                Deletes  a  timer  control block,  and removes  it  from  the 
  1933.                timeout queue.
  1934.  
  1935.  
  1936.           void change_timer (tlinkptr elem, dword tout, byte rept);
  1937.  
  1938.                Changes  the  timeout  value and/or the repeat  flag  for  an 
  1939.                existing timer control block. If "tout" is zero, the call has 
  1940.                the same effect as delete_timer.
  1941.  
  1942.  
  1943.           NOTE:  delete_timer  and change_timer should not be used on  auto-
  1944.           CTask Manual          Version 1.1  88-07-01                Page 37
  1945.  
  1946.  
  1947.  
  1948.           matically  allocated timer control blocks.  Since such blocks  are 
  1949.           deallocated   once   the  timeout  expires  (except   for   repeat 
  1950.           operations), the validity of the pointer is not guaranteed.
  1951.  
  1952.  
  1953.                                   Event Operations
  1954.                                   ================
  1955.  
  1956.                                       Resources
  1957.  
  1958.           A Resource is either in use or free, the default state is free.
  1959.           If a task has requested a resource, its state is in use.
  1960.           Tasks  requesting  a  resource  while it is in use  will  be  made 
  1961.           waiting. If the resource is released, the highest priority waiting 
  1962.           task is made eligible,  and is assigned the  resource.   Interrupt 
  1963.           handlers    may   not   use   resource   functions   other    than 
  1964.           "check_resource".
  1965.  
  1966.  
  1967.           resourceptr create_resource (resourceptr rsc [, byteptr name]);
  1968.  
  1969.                This initialises a resource.  Must be used prior to any other 
  1970.                operations on a resource.
  1971.  
  1972.                Returns the address of the control block, NULL on error.
  1973.  
  1974.                "rsc"     is  the resource control block (NULL for  automatic 
  1975.                          allocation).
  1976.  
  1977.                "name"    is   the  name  of  the  resource,   up  to   eight 
  1978.                          characters, plus zero-terminator. This parameter is 
  1979.                          defined only if TSK_NAMEPAR is enabled in tskconf.h.
  1980.  
  1981.  
  1982.           void delete_resource (resourceptr rsc);
  1983.  
  1984.                Calling  this  routine is optional.  It will kill  all  tasks 
  1985.                waiting for the resource.
  1986.  
  1987.  
  1988.           void release_resource (resourceptr rsc);
  1989.  
  1990.                Release the resource.  If the task does not own the resource, 
  1991.                the call is ignored.
  1992.                If  tasks  are waiting,  the highest priority task will  gain 
  1993.                access to the resource.
  1994.  
  1995.  
  1996.           int request_resource (resourceptr rsc, dword timeout);
  1997.  
  1998.                Requests the resource.  If it is not available,  the task  is 
  1999.                suspended. A timeout may be specified.
  2000.                Returns  0  if  resource  was  allocated,  -1  if  a  timeout 
  2001.                occurred, -2 on wake.
  2002.           CTask Manual          Version 1.1  88-07-01                Page 38
  2003.  
  2004.  
  2005.  
  2006.                This  call  is  ignored (returns a 0)  if  the  calling  task 
  2007.                already owns the resource.
  2008.  
  2009.  
  2010.           int c_request_resource (resourceptr rsc);
  2011.  
  2012.                Requests the resource only if it is available.
  2013.                Returns 0 if resource was allocated, -1 if unavailable.
  2014.  
  2015.  
  2016.           int check_resource (resourceptr rsc);
  2017.                 
  2018.                Returns 0 if resource is allocated, 1 if free.
  2019.  
  2020.  
  2021.                                         Flags
  2022.  
  2023.           A Flag can be either on or off, the default state is off (0).
  2024.           Tasks  can  wait  on either state of the flag.  If  the  state  is 
  2025.           changed,  all  tasks  waiting  for the state  are  made  eligible.  
  2026.           Interrupt  handlers  may use  the  "set_flag",  "clear_flag",  and 
  2027.           "check_flag" functions.
  2028.  
  2029.  
  2030.           flagptr create_flag (flagptr flg [, byteptr name]);
  2031.  
  2032.                This  initialises  a flag.  Must be used prior to  any  other 
  2033.                operations on a flag. The state is set to 0.
  2034.  
  2035.                Returns the address of the control block, NULL on error.
  2036.  
  2037.                "flg"     is the flag control block (NULL for automatic allo-
  2038.                          cation).
  2039.  
  2040.                "name"    is  the name of the flag,  up to eight  characters, 
  2041.                          plus  zero-terminator.  This  parameter is  defined 
  2042.                          only if TSK_NAMEPAR is enabled in tskconf.h.
  2043.  
  2044.  
  2045.           void delete_flag (flagptr flg);
  2046.  
  2047.                Calling  this  routine is optional.  It will kill  all  tasks 
  2048.                waiting for the flag.
  2049.  
  2050.  
  2051.           void set_flag (flagptr flg);
  2052.  
  2053.                This sets the flag.  All tasks waiting for the set state will 
  2054.                be made eligible for running.
  2055.  
  2056.  
  2057.           CTask Manual          Version 1.1  88-07-01                Page 39
  2058.  
  2059.  
  2060.  
  2061.           void clear_flag (flagptr flg);
  2062.  
  2063.                This  clears the flag.  All tasks waiting for the clear state 
  2064.                will be made eligible for running.
  2065.  
  2066.                       
  2067.           int wait_flag_set (flagptr flg, dword timeout);
  2068.  
  2069.                Waits for the set state of the flag.  If the flag is not set, 
  2070.                the task is suspended. A timeout may be specified.
  2071.                Returns 0 if the flag was set, -1 on timeout, -2 on wake.
  2072.  
  2073.  
  2074.           int wait_flag_clear (flagptr flg, dword timeout);
  2075.  
  2076.                Waits  for the clear state of the flag.  If the flag  is  not 
  2077.                clear, the task is suspended. A timeout may be specified.  
  2078.  
  2079.                Returns 0 if the flag was cleared, -1 on timeout, -2 on wake.
  2080.  
  2081.  
  2082.           int clear_flag_wait_set (flagptr flg, dword timeout)
  2083.  
  2084.                Combines the operations clear_flag and wait_flag_set.
  2085.                Returns 0 if the flag was set, -1 on timeout, -2 on wake.
  2086.  
  2087.           int check_flag (flagptr flg);
  2088.  
  2089.                Returns 0 if flag clear, 1 if set.
  2090.  
  2091.  
  2092.  
  2093.                                       Counters
  2094.  
  2095.           A  Counter can have any value from 0L to 0xffffffffL,  the default 
  2096.           value is 0. Tasks can wait for a counter being zero or non-zero.
  2097.           If the counter is cleared or decremented to zero,  all tasks  wai-
  2098.           ting for the zero condition are made eligible.
  2099.           If  the counter is incremented,  the highest priority task waiting 
  2100.           for non-zero is made eligible,  and the counter is decremented  by 
  2101.           one.
  2102.           Interrupt handlers may use the "clear_counter", "inc_counter", and 
  2103.           "check_counter" functions.
  2104.  
  2105.  
  2106.           counterptr create_counter (counterptr cnt [, byteptr name]);
  2107.  
  2108.                This  initialises a counter.  Must be used prior to any other 
  2109.                operations on a flag. The value is set to 0.
  2110.  
  2111.                Returns the address of the control block, NULL on error.
  2112.  
  2113.                "cnt"     is  the  counter control block (NULL for  automatic 
  2114.                          allocation).
  2115.           CTask Manual          Version 1.1  88-07-01                Page 40
  2116.  
  2117.  
  2118.  
  2119.  
  2120.                "name"    is the name of the counter, up to eight characters, 
  2121.                          plus  zero-terminator.  This parameter  is  defined 
  2122.                          only if TSK_NAMEPAR is enabled in tskconf.h.
  2123.  
  2124.  
  2125.           void delete_counter (counterptr cnt);
  2126.  
  2127.                Calling  this  routine is optional.  It will kill  all  tasks 
  2128.                waiting for the counter.
  2129.  
  2130.  
  2131.           void clear_counter (counterptr cnt);
  2132.  
  2133.                Clears  the counter to zero.  All tasks waiting for the  zero 
  2134.                state will be made eligible for running.
  2135.  
  2136.  
  2137.           int wait_counter_set (counterptr cnt, dword timeout);
  2138.  
  2139.                Waits for the counter having a nonzero value. If the value is 
  2140.                zero,  the task is suspended.  The value is decremented  when 
  2141.                the task gets access to the counter.  A timeout may be speci-
  2142.                fied.
  2143.                Returns  0 if the counter was nonzero,  -1 on timeout,  -2 on 
  2144.                wake.  
  2145.  
  2146.  
  2147.           int wait_counter_clear (counterptr cnt, dword timeout);
  2148.  
  2149.                Waits  for the counter having a zero value.  If the value  is 
  2150.                nonzero, the task is suspended. A timeout may be specified.
  2151.                Returns 0 if the counter was zero, -1 on timeout, -2 on wake.
  2152.  
  2153.  
  2154.           void inc_counter (counterptr cnt);
  2155.  
  2156.                Increments the counter.  If tasks are waiting for the nonzero 
  2157.                state,  the  highest  priority task is given  access  to  the 
  2158.                counter.
  2159.  
  2160.  
  2161.           dword check_counter (counterptr cnt);
  2162.  
  2163.                Returns the current value of the counter.
  2164.  
  2165.  
  2166.  
  2167.           CTask Manual          Version 1.1  88-07-01                Page 41
  2168.  
  2169.  
  2170.  
  2171.                                       Mailboxes
  2172.  
  2173.           A Mailbox can hold any number of mail blocks.
  2174.           Tasks  can send mail to a mailbox and wait for mail to  arrive.  A 
  2175.           mail block is assigned to the highest priority waiting task.  Care 
  2176.           must  be  exercised not to re-use a mail block until it  has  been 
  2177.           processed  by the receiving task.   The mail block format is  user 
  2178.           defineable,  with the first doubleword in a block reserved for the 
  2179.           tasking  system.   Interrupt  handlers  may use  the  "send_mail", 
  2180.           "c_wait_mail", and "check_mailbox" functions.
  2181.  
  2182.           Note  that mailboxes are well suited to provide the mechanism  for 
  2183.           managing a chain of (equally sized) free mail blocks. On initiali-
  2184.           sation,  all  free  blocks  would be "sent" to  the  manager  box. 
  2185.           Requesting a free block would use "wait_mail",  and freeing a used 
  2186.           block would again "send" it to the manager mailbox.
  2187.  
  2188.  
  2189.           mailboxptr create_mailbox (mailboxptr box [, byteptr name]);
  2190.  
  2191.                This initialises a mailbox.  Must be used prior to any  other 
  2192.                operations on a mailbox.
  2193.  
  2194.                Returns the address of the control block, NULL on error.
  2195.  
  2196.                "box"     is  the mailbox control block (NULL  for  automatic 
  2197.                          allocation).
  2198.  
  2199.                "name"    is the name of the mailbox, up to eight characters, 
  2200.                          plus  zero-terminator.  This  parameter is  defined 
  2201.                          only if TSK_NAMEPAR is enabled in tskconf.h.
  2202.  
  2203.  
  2204.           void delete_mailbox (mailboxptr box);
  2205.  
  2206.                Calling  this  routine is optional.  It will kill  all  tasks 
  2207.                waiting for mail.
  2208.  
  2209.  
  2210.           void send_mail (mailboxptr box, farptr msg);
  2211.  
  2212.                Sends  a message to the specified mailbox.  If tasks are wai-
  2213.                ting for mail, the highest priority task will get it.  
  2214.  
  2215.  
  2216.           farptr wait_mail (mailboxptr box, dword timeout);
  2217.  
  2218.                Waits for mail.  If no mail is available, the task is suspen-
  2219.                ded. A timeout may be specified.
  2220.                Returns the pointer to the received mail block, TTIMEOUT 
  2221.                (-1) on timeout, TWAKE (-2) on wake.
  2222.  
  2223.  
  2224.           CTask Manual          Version 1.1  88-07-01                Page 42
  2225.  
  2226.  
  2227.  
  2228.           farptr c_wait_mail (mailboxptr box);
  2229.  
  2230.                Reads mail only if mail is available.
  2231.                Returns  NULL  if there is no mail,  else a  pointer  to  the 
  2232.                received message.
  2233.  
  2234.  
  2235.           int check_mailbox (mailboxptr box);
  2236.  
  2237.                Returns 0 if mailbox is empty, 1 otherwise.
  2238.  
  2239.  
  2240.  
  2241.                                         Pipes
  2242.  
  2243.           A Pipe has a buffer to hold character or word sized items.
  2244.           An  item may be written to a pipe if there is space in the buffer. 
  2245.           Otherwise,  the writing task will be made waiting.  A reading task 
  2246.           will be made waiting if the buffer is empty.   If an item has been 
  2247.           read,  the highest priority task waiting to write to the pipe will 
  2248.           be allowed to write.
  2249.           Interrupt  handlers  may  only use  pipe  functions  "check_pipe", 
  2250.           "c_write_pipe", and "c_read_pipe".
  2251.  
  2252.           Note  that  the  values -1 and -2 (0xffff and  0xfffe)  should  be 
  2253.           avoided when writing to word pipes.  These values are used to mark 
  2254.           timeout and wake when reading,  or pipe empty when checking a word 
  2255.           pipe, and thus may lead to erroneous operation of your routines.
  2256.  
  2257.  
  2258.           pipeptr create_pipe (pipeptr pip, farptr buf, word bufsize
  2259.                                [, byteptr name]);
  2260.           wpipeptr create_wpipe (wpipeptr pip, farptr buf, word bufsize
  2261.                                  [, byteptr name]);
  2262.  
  2263.                This  initialises  a pipe.  Must be used prior to  any  other 
  2264.                operations on a pipe.  "bufsize" specifies the buffer size in 
  2265.                bytes.  With word pipes,  the buffer size should be divisible 
  2266.                by two.
  2267.  
  2268.                Returns the address of the control block, NULL on error.
  2269.  
  2270.                "pip"     is the pipe/wpipe control block (NULL for automatic 
  2271.                          allocation).
  2272.  
  2273.                "name"    is  the name of the pipe,  up to eight  characters, 
  2274.                          plus  zero-terminator.  This  parameter is  defined 
  2275.                          only if TSK_NAMEPAR is enabled in tskconf.h.
  2276.  
  2277.  
  2278.           CTask Manual          Version 1.1  88-07-01                Page 43
  2279.  
  2280.  
  2281.  
  2282.           void delete_pipe (pipeptr pip);
  2283.           void delete_wpipe (wpipeptr pip);
  2284.  
  2285.                Calling  this  routine is optional.  It will kill  all  tasks 
  2286.                waiting to read or write messages.
  2287.  
  2288.  
  2289.           int read_pipe (pipeptr pip, dword timeout);
  2290.           word read_wpipe (wpipeptr pip, dword timeout);
  2291.  
  2292.                Read an item from a pipe.  If no item is available,  the task 
  2293.                is suspended. A timeout may be specified.
  2294.                Returns the item, or -1 on timeout, -2 on wake.
  2295.  
  2296.  
  2297.           int c_read_pipe (pipeptr pip);
  2298.           word c_read_wpipe (wpipeptr pip);
  2299.  
  2300.                Reads an item from a pipe only if one is available.
  2301.                Returns the received item, or -1 if none is available.
  2302.  
  2303.  
  2304.           int write_pipe (pipeptr pip, byte ch, dword timeout);
  2305.           int write_wpipe (wpipeptr pip, word ch, dword timeout);
  2306.  
  2307.                Writes an item to a pipe.  If the buffer is full, the task is 
  2308.                suspended. A timeout may be specified.
  2309.                If  tasks are waiting to read,  the item will be assigned  to 
  2310.                the highest priority waiting task.
  2311.                Returns 0 on success, or -1 on timeout, -2 on wake.
  2312.                       
  2313.  
  2314.           int c_write_pipe (pipeptr pip, byte ch);
  2315.           int c_write_wpipe (wpipeptr pip, word ch);
  2316.  
  2317.                Writes an item to a pipe only if enough space is available.
  2318.                Returns 0 on success, or -1 if no space available.
  2319.                       
  2320.  
  2321.           int wait_pipe_empty (pipeptr pip, dword timeout);
  2322.           int wait_wpipe_empty (wpipeptr pip, dword timeout);
  2323.  
  2324.                Waits  for  the pipe to be emptied.  If the pipe  is  already 
  2325.                empty  on  entry,  the task continues to run.  Returns  0  on 
  2326.                empty, -1 on timeout, -2 on wake.
  2327.  
  2328.  
  2329.           int check_pipe (pipeptr pip);
  2330.           word check_wpipe (pipeptr pip);
  2331.  
  2332.                Returns  -1 if the pipe is empty,  else the first item in the 
  2333.                pipe. The item is not removed from the pipe.
  2334.  
  2335.  
  2336.           CTask Manual          Version 1.1  88-07-01                Page 44
  2337.  
  2338.  
  2339.  
  2340.           word pipe_free (pipeptr pip);
  2341.           word wpipe_free (wpipeptr pip);
  2342.  
  2343.                     Returns the number of free items available in the pipe.
  2344.  
  2345.  
  2346.  
  2347.                                         Buffers
  2348.  
  2349.           A Buffer has a buffer to hold message strings.
  2350.           A  message may be written to a buffer if it fits into the  buffer. 
  2351.           Otherwise,  the writing task will be made waiting.  A reading task 
  2352.           will  be made waiting if the buffer is empty.   If a  message  has 
  2353.           been  read,  the  highest  priority task waiting to write  to  the 
  2354.           buffer  will  be  allowed to write if its message  fits  into  the 
  2355.           available space.
  2356.           Interrupt  handlers  may  not  use  buffer  functions  other  than 
  2357.           "check_buffer".
  2358.           The buffer routines are implemented using resources and pipes, and 
  2359.           thus are not part of the true "kernel" routines.
  2360.  
  2361.  
  2362.           bufferptr create_buffer (bufferptr buf, farptr pbuf, word bufsize
  2363.                                    [, byteptr name]);
  2364.  
  2365.                This  initialises a buffer.  Must be used prior to any  other 
  2366.                operations on a buffer.
  2367.                The minimum buffer size is the length of the longest expected 
  2368.                message plus two.
  2369.  
  2370.                Returns the address of the control block, NULL on error.
  2371.  
  2372.                "buf"     is  the  buffer control block (NULL  for  automatic 
  2373.                          allocation).
  2374.  
  2375.                "name"    is the name of the buffer,  up to eight characters, 
  2376.                          plus  zero-terminator.  This  parameter is  defined 
  2377.                          only if TSK_NAMEPAR is enabled in tskconf.h.
  2378.  
  2379.  
  2380.           void delete_buffer (bufferptr buf);
  2381.  
  2382.                Calling  this  routine is optional.  It will kill  all  tasks 
  2383.                waiting to read or write messages.
  2384.  
  2385.  
  2386.           CTask Manual          Version 1.1  88-07-01                Page 45
  2387.  
  2388.  
  2389.  
  2390.           int read_buffer (bufferptr buf, farptr msg, int size, 
  2391.                            dword timeout);
  2392.  
  2393.                Read a message from a buffer. If no message is available, the 
  2394.                task is suspended. A timeout may be specified.
  2395.                The  message  will  be copied to the  buffer  "buf",  with  a 
  2396.                maximum length of "size" bytes.
  2397.                Returns the length of the received message, -1 on timeout, -2 
  2398.                on wake.
  2399.  
  2400.  
  2401.           int c_read_buffer (bufferptr buf, farptr msg, int size);
  2402.  
  2403.                Reads  a  message  from a buffer only if  one  is  available.  
  2404.                Returns the length of the received message,  or -1 if none is 
  2405.                available.
  2406.  
  2407.  
  2408.           int write_buffer (bufferptr buf, farptr msg, int size, 
  2409.                             dword timeout);
  2410.  
  2411.                Writes  a message from "msg" with length "size" to a  buffer. 
  2412.                If not enough space for the message is available, the task is 
  2413.                suspended. A timeout may be specified.
  2414.                If tasks are waiting for messages,  the highest priority task 
  2415.                will get it.
  2416.                Returns the length of the message, -1 on timeout, -2 on wake, 
  2417.                -3 on error (length < 0 or length > buffer buffer size).
  2418.  
  2419.                       
  2420.           int c_write_buffer (bufferptr buf, farptr msg, int size);
  2421.  
  2422.                Writes  a  message  to  a buffer  only  if  enough  space  is 
  2423.                available.
  2424.                Returns the length of the message,  -1 if no space available, 
  2425.                or -3 on error (length < 0 or length > buffer buffer size).
  2426.  
  2427.  
  2428.           word check_buffer (bufferptr buf);
  2429.  
  2430.                Returns  the  current number of bytes (not messages)  in  the 
  2431.                buffer, including the length words.
  2432.  
  2433.  
  2434.  
  2435.  
  2436.           CTask Manual          Version 1.1  88-07-01                Page 46
  2437.  
  2438.  
  2439.  
  2440.                                  The Keyboard Handler
  2441.                                  ====================
  2442.  
  2443.           word t_read_key (void)
  2444.  
  2445.                Waits for a key to be entered.  Returns the ASCII-code in the 
  2446.                lower  byte,  and  the  scan-code in the upper  byte,  or  -2 
  2447.                (0xfffe) on wake.
  2448.  
  2449.  
  2450.           word t_wait_key (dword timeout)
  2451.  
  2452.                Waits for a key to be entered.  Returns the ASCII-code in the 
  2453.                lower  byte,  and  the scan-code in the  upper  byte,  or  -1 
  2454.                (0xffff) on timeout, -2 (0xfffe) on wake.
  2455.  
  2456.  
  2457.           word t_keyhit (void)
  2458.  
  2459.                Returns -1 (0xffff) if no key was entered,  else the value of 
  2460.                the key. The key is not removed.
  2461.  
  2462.  
  2463.                                 The Serial I/O handler
  2464.                                 ======================
  2465.  
  2466.           The  serial I/O handler provides full duplex interrupt driven  I/O 
  2467.           on the serial ports. Support for COM1 and COM2 is included, adding 
  2468.           other  ports  is possible by changing the source  and/or  defining 
  2469.           ports on-line.
  2470.  
  2471.  
  2472.           int v24_define_port (int base, byte irq, byte vector)
  2473.  
  2474.                Defines  a  new COM-port. This routine can only  be  used  if 
  2475.                TSK_DYNAMIC is enabled.
  2476.  
  2477.                "base" is the port base I/O address.
  2478.  
  2479.                "irq" is the IRQ-line number (0-7 for XT, 0-15 for AT) the
  2480.                      port uses for interrupts.
  2481.  
  2482.                "vector" is the interrupt vector number (0-0xff).
  2483.  
  2484.                The  return  value is the internal port number for  use  with 
  2485.                v24_install. -1 is returned on error (memory full). 
  2486.  
  2487.  
  2488.           CTask Manual          Version 1.1  88-07-01                Page 47
  2489.  
  2490.  
  2491.  
  2492.           sioptr v24_install (int port, int init,
  2493.                               farptr rcvbuf, word rcvsize, 
  2494.                               farptr xmitbuf, word xmitsize);
  2495.  
  2496.                Installs the handler for the specified port. Currently, ports 
  2497.                0  (COM1) and 1 (COM2) are supported. Both ports may be  used 
  2498.                simultaneously,  the buffer areas for the ports must  not  be 
  2499.                shared.
  2500.  
  2501.                "rcvbuf" is a word pipe buffer area for received  characters, 
  2502.                with "rcvsize" specifying the buffer size in bytes. Note that 
  2503.                the  buffer  will  hold  rcvsize  /  2  received  characters. 
  2504.                "rcvbuf" may be NULL for automatic allocation.
  2505.  
  2506.                "xmitbuf"  is  a byte pipe buffer for characters  waiting  for 
  2507.                transmissions, "xmitsize" gives its size in bytes.
  2508.                "xmitbuf" may be NULL  for automatic allocation.
  2509.  
  2510.                If  the  port number for v24_install is ORed with  0x80,  the 
  2511.                port  is  *relative*. This means that the entry in  the  BIOS 
  2512.                table for COM-Ports is used to search the tables internal  to 
  2513.                the  driver  for the port information, instead of  using  the 
  2514.                table  entry directly. If the port address cannot  be  found, 
  2515.                the  driver returns with an error code. Note that  ports  are 
  2516.                numbered from 0, so to specify COM1, pass 0x80 as parameter.  
  2517.  
  2518.                If the "init" parameter is non-zero, the port is  initialised 
  2519.                with  the  default  values specified in the  source.  If  the 
  2520.                parameter  is zero, the control registers and baud  rates  on 
  2521.                entry are not modified.
  2522.  
  2523.                The  return value is a pointer to the sio control  block  for 
  2524.                use with the other driver routines. NULL is returned on error 
  2525.                (invalid   port   number,  bad  buffer   sizes,   nonexistent 
  2526.                hardware, out of memory).
  2527.  
  2528.  
  2529.           void v24_remove (sioptr sio, int restore);
  2530.  
  2531.                Removes the driver for the specified control block.
  2532.                Should  be called for all ports installed before exiting  the 
  2533.                program.
  2534.  
  2535.                If the "restore" parameter is nonzero, the control  registers 
  2536.                and  the baud rate that were set before installation  of  the 
  2537.                driver  are  restored. If the parameter is zero,  the  values 
  2538.                are not changed.
  2539.  
  2540.  
  2541.           CTask Manual          Version 1.1  88-07-01                Page 48
  2542.  
  2543.  
  2544.  
  2545.           void v24_remove_all (void)
  2546.  
  2547.                Removes  all  installed serial i/o drivers. This  routine  is 
  2548.                automatically   called  on  remove_tasker  if  drivers   were 
  2549.                installed. Note that you can not specify a restore parameter, 
  2550.                the default restore parameter is used (constant in tsksio.c).
  2551.  
  2552.  
  2553.           void v24_change_rts (sioptr sio, int on);
  2554.  
  2555.                Changes the state of the RTS output line. A nonzero value for 
  2556.                "on" will turn the output on.
  2557.  
  2558.  
  2559.           void v24_change_dtr (sioptr sio, int on);
  2560.  
  2561.                Changes the state of the DTR output line. A nonzero value for 
  2562.                "on" will turn the output on.
  2563.  
  2564.  
  2565.           extern void far v24_change_baud (sioptr sio, long rate);
  2566.  
  2567.                Changes  the baud rate.  The following baud rates are suppor-
  2568.                ted:
  2569.  
  2570.                50,  75, 110, 134, 150, 300, 600, 1200, 1800, 2000, 
  2571.                2400, 3600, 4800, 7200, 9600, 19200L, 38400L.
  2572.  
  2573.                Note  that baud rates above 9600 may cause problems  on  slow 
  2574.                machines.
  2575.  
  2576.           extern void far v24_change_parity (sioptr sio, int par);
  2577.  
  2578.                Changes  parity.  The parameter must be one of the  following 
  2579.                values defined in "sio.h":
  2580.  
  2581.                     PAR_NONE  no parity checks
  2582.                     PAR_EVEN  even parity
  2583.                     PAR_ODD   odd parity
  2584.                     PAR_MARK  mark parity
  2585.                     PAR_SPACE space parity
  2586.  
  2587.  
  2588.           extern void far v24_change_wordlength (sioptr sio, int len);
  2589.  
  2590.                Changes word length. Values 5, 6, 7, 8 may be given.
  2591.  
  2592.  
  2593.           extern void far v24_change_stopbits (sioptr sio, int n);
  2594.  
  2595.                Changes Stopbits. Values 1 and 2 are allowed.
  2596.  
  2597.  
  2598.           CTask Manual          Version 1.1  88-07-01                Page 49
  2599.  
  2600.  
  2601.  
  2602.           extern void far v24_watch_modem (sioptr sio, byte flags);
  2603.  
  2604.                The  modem  status  input  lines  specified  in  the  "flags" 
  2605.                parameter must be active to allow transmission.  Transmission 
  2606.                will  stop if one of the lines goes inactive.  The  parameter 
  2607.                may be combined from the following values defined in "sio.h":
  2608.  
  2609.                     CTS  to watch clear to send
  2610.                     DSR  to watch data set ready
  2611.                     RI   to watch ring indicator
  2612.                     CD   to watch carrier detect
  2613.  
  2614.                A  value  of  zero  (the  default)  will  allow  transmission 
  2615.                regardless of modem status.
  2616.  
  2617.  
  2618.           extern void far v24_protocol (sioptr sio, int prot, 
  2619.                                         word offthresh, word onthresh);
  2620.  
  2621.                Sets the handshake protocol to use.  The "prot" parameter may 
  2622.                be combined from the following values:
  2623.  
  2624.                     XONXOFF   to enable XON/XOFF (DC1/DC3) handshake
  2625.                     RTSCTS    to enable RTS/CTS handshake
  2626.  
  2627.                The  "offthresh"  value specifies the minimum number of  free 
  2628.                items in the receive buffer. If this threshold is reached, an 
  2629.                XOFF is transmitted and/or the RTS line is inactivated.
  2630.                The  "onthresh"  value specifies the minimum number of  items 
  2631.                that  must be free before XON is transmitted and/or  the  RTS 
  2632.                line is re-activated.
  2633.  
  2634.                Enabling XONXOFF will remove all XON and XOFF characters from 
  2635.                the input stream.  Transmission will be disabled when XOFF is 
  2636.                received, and re-enabled when XON is received.
  2637.  
  2638.                Enabling RTSCTS will stop transmission when the CTS modem 
  2639.                input line is inactive.
  2640.  
  2641.  
  2642.           int v24_send (sioptr sio, byte ch, dword timeout);
  2643.  
  2644.                Transmits the character "ch". A timeout may be specified. 
  2645.                Returns -1 on timeout, -2 on wake, else 0.
  2646.  
  2647.  
  2648.           CTask Manual          Version 1.1  88-07-01                Page 50
  2649.  
  2650.  
  2651.  
  2652.           int v24_receive (sioptr sio, dword timeout);
  2653.  
  2654.                Waits  for  a  received  character.   A  timeout  may  be 
  2655.                specified.  Returns -1 on timeout,  -2 on wake,  else the 
  2656.                character  in the lower byte,  plus an error code in  the 
  2657.                upper byte. The error code is the combination of
  2658.  
  2659.                     0x02      overrun error
  2660.                     0x04      parity error
  2661.                     0x08      framing error
  2662.                     0x10      break interrupt.
  2663.  
  2664.  
  2665.           int v24_check (sioptr sio);
  2666.  
  2667.                Returns -1 if no receive character is available, else the 
  2668.                next  character  from  the pipe.  The  character  is  not 
  2669.                removed.
  2670.  
  2671.  
  2672.           int v24_overrun (sioptr sio);
  2673.  
  2674.                Checks for receive pipe overrun.  Returns 1 if an overrun 
  2675.                occurred, 0 otherwise. Clears the overrun flag.
  2676.  
  2677.  
  2678.           int v24_modem_status (sioptr sio);
  2679.  
  2680.                Returns the current modem status word.  The CTS, DSR, RI, 
  2681.                and  CD  defines may be used to extract the  modem  input 
  2682.                line status.
  2683.  
  2684.  
  2685.           int v24_complete (sioptr sio);
  2686.  
  2687.                Returns  1 if all characters in the transmit pipe  have  been 
  2688.                sent, else 0.
  2689.  
  2690.  
  2691.           int v24_wait_complete (sioptr sio, dword timeout);
  2692.  
  2693.                Waits  for  the  transmit pipe to be  empty.  Returns  -1  on 
  2694.                timeout, -2 on wake, else 0.
  2695.  
  2696.  
  2697.  
  2698.           CTask Manual          Version 1.1  88-07-01                Page 51
  2699.  
  2700.  
  2701.  
  2702.                               The Printer Output Driver
  2703.                               =========================
  2704.  
  2705.           The  printer output driver provides for buffered output to  up  to 
  2706.           three  printer  ports (more can be added by editing  the  source). 
  2707.           Interrupt  or polling may be selected. Due to the  usual  hardware 
  2708.           implementation  of  printers  and  the  printer  interface,  using 
  2709.           polling  is  recommended. When using interrupts,  you  should  not 
  2710.           simultaneously  install  both  port 0 and port  1  with  interrupt 
  2711.           enabled, since both ports share the same interrupt line.
  2712.  
  2713.  
  2714.           int prt_install (int port, byte polling, word prior, 
  2715.                            farptr xmitbuf, word xmitsize);
  2716.  
  2717.                Installs the printer driver for the specified port.  Ports  0 
  2718.                (LPT1), 1 (LPT2), and 2 (LPT3) are supported.
  2719.  
  2720.                The   "polling"  parameter  specifies  polling  output   when 
  2721.                nonzero, interrupt output when zero.
  2722.  
  2723.                The "prior" parameter sets the priority of the printer output 
  2724.                task.
  2725.  
  2726.                "xmitbuf"  is  a buffer area for the printer  output  buffer, 
  2727.                "xmitsize" specifies its size in bytes.
  2728.                The  buffer  pointer  may be specified as  NULL  for  dynamic 
  2729.                allocation if TSK_DYNAMIC is enabled.
  2730.  
  2731.                If  the  port number for prt_install is ORed with  0x80,  the 
  2732.                port  is  *relative*. This means that the entry in  the  BIOS 
  2733.                table for LPT-Ports is used to search the tables internal  to 
  2734.                the  driver  for the port information, instead of  using  the 
  2735.                table  entry directly. If the port address cannot  be  found, 
  2736.                the  driver returns with an error code. Note that  ports  are 
  2737.                numbered from 0, so to specify LPT1, pass 0x80 as parameter.  
  2738.  
  2739.                The return value is the internal port number for use with the 
  2740.                other driver routines. -1 is returned on error (invalid  port 
  2741.                number, bad buffer sizes, nonexistent hardware).
  2742.  
  2743.  
  2744.           void prt_remove (int port);
  2745.  
  2746.                Removes  the  printer  driver for  the  specified  port.
  2747.  
  2748.  
  2749.           void prt_remove_all (void)
  2750.  
  2751.                Removes  all installed printer ports. This routine  is  auto-
  2752.                matically called on remove_tasker if ports were installed.
  2753.  
  2754.  
  2755.           CTask Manual          Version 1.1  88-07-01                Page 52
  2756.  
  2757.  
  2758.  
  2759.           void prt_change_control (int port, byte control);
  2760.  
  2761.                Changes  the  printer control output lines of the  port.  The 
  2762.                value for "control" may be combined from the following values 
  2763.                defined in "prt.h":
  2764.  
  2765.                     AUTOFEED  will enable printer auto feed if set
  2766.                     INIT      will initialise (prime) the printer if clear
  2767.                     SELECT    will select the printer if set
  2768.  
  2769.  
  2770.           int prt_write (int port, byte ch, dword timeout);
  2771.  
  2772.                Write a byte to the printer.  A timeout may be given. Returns 
  2773.                0 on success, -1 on timeout, -2 on wake.
  2774.  
  2775.  
  2776.           int prt_status (int port);
  2777.  
  2778.                Returns the current printer status lines,  combined from  the 
  2779.                values
  2780.  
  2781.                     BUSY      Printer is busy when 0
  2782.                     ACK       Acknowledge (pulsed 0)
  2783.                     PEND      Paper End detected when 0
  2784.                     SELIN     Printer is selected (on line) when 1
  2785.                     ERROR     Printer error when 0
  2786.  
  2787.  
  2788.           int prt_complete (int port);
  2789.  
  2790.                Returns 1 if the printer buffer has been completely transmit-
  2791.                ted, 0 otherwise.
  2792.  
  2793.  
  2794.           int prt_wait_complete (int port, dword timeout);
  2795.  
  2796.                Waits for printer output to complete.  Returns 0 on  success, 
  2797.                else -1 on timeout, -2 on wake.
  2798.  
  2799.                                                                                                                                
  2800.